Suppose I have static C libraries lib1 and lib2 where functions in lib2 call functions in lib1. I've made crates lib1-sys and lib2-sys which simply list the extern functions exposed by each library, lib2-sys depends on lib1-sys. The Cargo.toml file for each lists the corresponding library in its link field and the build.rs for each just gives cargo:rustc-link-search and cargo:rustc-link-lib.
My issue is that when I try and build a binary depending on lib2-sys (either as a separate crate or just building tests for lib2-sys), cargo doesn't link against lib1 so I get unresolved symbol errors from the functions in lib2 which call out to lib1. That is, depending on lib1-sys apparently isn't enough to guarantee linking against lib1. Is that expected behaviour? I found two fixes which seem to work, which is prefered or is there anything better?
I can put extern crate lib1-sys; at the top of lib2-sys/lib.rs.
I can add cargo:rustc-link-lib=lib1 to lib2-sys/build.rs.
Related
I have read Managing Growing Projects with Packages, Crates, and Modules, but I still don't really understand the hierarchy. I know what is crate and that there should be at least one crate in package: max 1 library crate and 0 or more binary crates.
First: Assume I want to have both lib.rs and main.rs crates in package. How do I access/call functions from lib.rs in main.rs?
Second: when I create new library with cargo new --lib library-name it creates directory with that name and bunch of files there, the only way I figured out to call functions from that library in src/main.rs is:
mod some_library;
use crate::library_name::library_name::foo;
fn main() {
foo();
}
// Filename: src/library_name.rs
pub mod library_name; // I don't really understand this
// Filename: src/library_name/library_name.rs
pub fn foo() {
// ...
}
where I have the following hierarchy:
- package_name
- src
- library_name
- src
- lib.rs
- Cargo.toml
- library_name.rs
- library_name.rs
- main.rs
- Cargo.toml
Is it necessary for src/library_name.rs to have the same name as src/library_name library? I'm really confused.
It's a hierarchy of three or four levels.
Workspace (optional): At the top there's the workspace. A workspace consists of one or more packages. The Cargo.toml file is special and more or less only lists the workspace members.
Workspaces are optional and used for big projects. Smaller projects with only one package don't need them. In such cases we can ignore workspaces. We have a package at the top and the hierarchy is only three levels deep.
Package: Then there's the package. A package is what haves a real Cargo.toml file. A package can compile to crates.
Crate: A crate is a library or an executable compiled from the package. A library crate has a lib.rs file as a starting point. An executable crate has a main function as a starting point. A package can compile to at most one library and several executables.
Module: Then there are modules. Modules are the hierarchical way how Rust organizes items like struct, enum, functions and others in the source code.
And how to you use this to give items unique names?
Answer: A fully qualified name starts with the package, then a series of module names then finally the item's name.
An example:
serde::de::value::StringDeserializer (see here) has as package serde, as module path de then value then the struct is called StringDeserializer.
And where's the crate?
The package serde has only one crate, the library. If you look at the package's Cargo.toml there are no binaries listed.
(People tend to be confused about packages and crates. Even sometimes I am not precise and say "the serde crate" when I mean the package.)
In short: A package is code under a name (for example serde). A crate is a library or an executable.
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.
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
I'm trying to read the code of servo. As an example, I'm looking at this code in layout_task.rs:
use url::Url;
..and I want to know which code this refers to (the answer is rust-url).
Per the Rust reference §6.1.2.2 Use declarations,
the paths contained in use items are relative to the crate root [...]
It is also possible to use self and super at the beginning of a use item to refer to the current and direct parent modules respectively.
All rules regarding accessing declared modules in use declarations apply to both module declarations and extern crate declarations.
The reference (§5 Crates and source files) does not explicitly say what a "crate root" is, but it does share that:
A crate contains a tree of nested module scopes. The top level of this tree is a module that is anonymous [...] 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.
So it seems that to find the crate root that the current file (layout_task.rs) belongs to, we need to figure out what source file rustc is invoked with when building the crate. With Cargo this is specified in Cargo.toml and defaults to src/lib.rs:
[lib]
path = "src/lib.rs"
In my case, here's Cargo.toml and the lib.rs has:
extern crate url;
pub mod layout_task;
So far so good. To find out what the extern crate refers to, we need to look at Cargo.toml again:
[dependencies.url]
version = "0.2"
The cargo docs claim that "Dependencies from crates.io are not declared with separate sections", but apparently they can be... So we look the package up on crates.io: https://crates.io/crates/url
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.