Cargo equivalent of rustc --extern

Hi Friends,
I am new to Rust (like 2 days old). With cargo, I created a test library( cargo new utils --lib). I am able to build it and have got the outupt utils.rlib by running 'cargo build' at the utils directory. I want to create a test binary (say caller.exe) that uses this library. So, I copied the utils.rlib file to a directory lib (as a sibling to the caller's src directory.
I am able to build the caller.exe with the rustc at the 'caller' directory as given below,

rustc src\main.rs --extern utils=lib\libutils.rlib -o caller.exe

Instead of using rustc explicitly, I want to use 'cargo build' to generate caller.exe. Would you please tell me what to put in the cargo.toml file? (specifically, I want to use the already generted utils.rlib file, instead of building it from its source code again.)

I searched the rust documentation, but failed to find the solution.

Thanks,
Gopan

I don't think you want to be copying the rlib around. If I understand what you're trying to do, you want to have a library and a binary application that uses the library, both written in Rust? If so, you probably want a Cargo workspace.

You can create a workspace by:

  1. Creating a directory to house the workspace
  2. Creating a Cargo.toml file in the root of the folder that has something like this in it:
[workspace]
members = ["my-library", "my-binary"]
  1. Then you can run cargo new --lib my-library and cargo new my-binary to create your binary and your library
  2. And then you can depend on your library by adding this to your binary's Cargo.toml:
[dependencies]
my_library = { path = "../my-library" }

You almost never need to call rustc directly, and if you do you will probably need to supply many options. cargo provides convenient commands to manage your project; I recommend you read some of the Cargo book if you have not already.

If you do for some reason need to call rustc directly, my understanding is that you should use the cargo rustc -- [YOUR_RUSTC_OPTIONS] command.

1 Like

Hi camelid, Thanks for the reply.

Say, I am writing a library and supplying to the customers, for my business. Naturally I will not ship the source code. In C++/Windows, we ship the required header files (for compile time), .lib file (for link time), and the .dll (for runtime). I am trying to do the same scenario in Rust.

  1. To me, it looked like that .rlib file is a Rust static library, and I am able to link it with the client application through rustc. So, this being a very ubiquitous build scenario, I thought cargo would support it. What is wrong with my expectation, :slight_smile: ?

  2. In fact, I would like to know how to link the .lib/.dll file (like .rlib) with the rust binary through both cargo and rustc. Because, that would be what I will be doing in my business environment where I am employed. By mentioning crate-type = ["dylib"] in the [lib] section of the library's cargo file, I got the proper .lib file and .dll file. But, I couldn't find a way to link it with the rust binary. I verified the linkability of these .lib and .dll file writing a C Program (with the help of visual studio).

Regards,
Gopan

Kinda. Think of it more as an internal object file used by rustc. The fact that you can use it as a normal object file is an implementation detail and you can't really rely on it.

What you are looking for is setting the crate-type to cdylib (to get a *.dll or *.so for dynamic linking) or staticlib (to get a *.a that can be statically linked in).

Have a skim through this page of the cargo docs. It explains how you can tweak Cargo.toml to emit compiled artifacts that can be used from other languages.

Setting the crate-type is mainly used when interoperating with other languages. If one Rust crate needs to link with another, add it to the [dependencies] section under Cargo.toml and cargo will make sure things are correctly compiled and linked.

You don't normally want to link to a Rust crate's *.dll or *.a directly because Rust makes no guarantees around its ABI or type layouts. I've been bitten by this in the past - see Michael-F-Bryan/rust-ffi-guide#79 and the resulting rust-lang/rust#67179.

1 Like

From the examples of [dependencies], what I understand is that it is the the means of how to get the the source code of the dependency that we put under [dependencies]. (I saw examples of fetching source code from github, and the local path in the machine. I tested this too.). Am I correct?
In that case, how is the use-case I mentioned (deployment of library files to users) achieved? If Rust is yet to come up with that, no problem; I shall wait. If it is already available, I would like to use it. ( I will anyway go through the links you gave me and come back ).

Thanks.
Gopan

I'm not too familiar with this part of the Rust world, but I think you will have to build a layer that goes through C-based FFI. You don't actually need any C code, but I don't think you can distribute it like a normal Rust library, and you may need a wrapper around the C based api for your users to use it through.

crate-type = ["dylib"] is meant for linking like any other rust crate. This means that you will need to use the same rustc version when using it as when creating it. There is also crate-type = ["cdylib"] which creates "regular" dylibs that are linkable from C code and usable in rust using regular C ffi.

The rlib and dylib crate types have a lot of additional metadata required to use them from other rust crates. This includes but is not limited to:

  • positions of all function, type, ... definitions in the source code
  • full type layout including original field names
  • the name of every definition in your crate, whether private or public (for functions and statics included in the symbol table anyway)
  • the MIR of all #[inline] and generic functions and all consts. (this is an intermediate representation of function bodies)

I have gone through the links you provided. They contain lot of useful information. All of them are dealing with how a crate-type = cdylib can be loaded in rust binary entirely at runtime. When I am in a situation to use a C-dll, I will use those techniques.

But right now I am specifically looking for how to link them through cargo. And, as you said Rust making no guarantees around its ABI, let us postpone discussion of linking DLL files. So, my question tapers down to how to use .rlib files in cargo. Wording it another way, 'what is the cargo equivalent of the below command?' Or, is there no cargo equivalence for that.

rustc src\main.rs --extern utils=lib\libutils.rlib -o caller.exe

Why I am so obsessed with Cargo file is that I got the notion that a cargo.toml stands for the makefile script for building the package in Rust. Only that is thought that makes me argue like 'if it is possible through rustc, why is it not possible through cargo.".

Please don't think I am rude. In fact, I am not trying to get something done for my personal/official purpose. Instead, my question is purely of a theoretical nature as I am a Rust student.

Thank you,
Gopan

Mmmm kinda, but not really.

Makefiles and Cargo.toml are both tool for telling the build system how to compile your project, but they differ in that a Makefile executes a series of shell commands and automates the calling of your normal C compiler.

On the other hand, a Cargo.toml is more high-level/declarative in that you tell cargo "hey, see these items under the [dependencies] section? They tell you how to get the source code for some Rust crates I'd like you to link into my project". It's then up to cargo to figure out how that is done (you can see the commands being executed with cargo build --verbose).

Simply put, you don't. Pretend *.rlib files don't exist.

The [dependencies] section in Cargo.toml is the way to link to another library. Under the hood cargo will compile your dependencies to rlibs and make sure rustc links things together correctly, but in your day to day life as a Rust developer you'll never touch an rlib.

So cargo and Cargo.toml are actually doing what you are asking for, you just don't see it because they abstract out a lot of the detail around caching, build order, passing the right flags to rustc, and so on.

If this all feels magical I'd recommend using the --verbose flag the next time you cargo build or cargo test. That'll spew out all the calls to rustc and you use that to figure out what's going on.

2 Likes

You shouldn't want to distribute .rlib files. At best you can distribute a .dll combined with the source code of a rust wrapper if you don't want to distribute the source code of your crate. As I already said .rlib files leak a lot about the source code. In fact I think it should be possible to reconstruct something that resembles chunks of the original source without a lot of effort. (excluding comments and stuff like that) I don't say it is trivial, but it is a lot more trivial than looking at the disassembly.