Referencing crate data types and functions from build script - rust

I have a rust binary crate (well, it also has a lib.rs file for tests), and am trying to write a build script. This build script needs to generate a JSON file from a static rust object (custom struct crate::datatypes::ErrorMarkup) using serde, which gets imported by the binary crate. I know I could just reference the static object in the binary crate, but the binary crate must import a JSON file that may eventually come from some other source or may be modified between the build and run phases (i.e. the intent is to ship the built file and the JSON file).
How do I reference crate data types and functions from a build script?
Project structure:
- cargo.lock
- cargo.toml
- markup.json (target file)
- build.rs
- src
- main.rs
- lib.rs (exports)
- datatypes.rs
- tests
- verify.rs
I have tried both use pump_log_tool (my crate) and use crate::src and a billion other variations.
I understand this may be an issue due to build dependencies and runtime dependencies. Is there an obviously better way to achieve this goal?

A build script can't depend on the crate it builds
Cargo compiles build.rs into its own crate with its own dependencies. By making your build script depend on the crate it's building, you create a circular dependency that Cargo can't resolve.
Move the needed types to a different crate
The typical solution is to move the items that are (presumably) defined in datatypes.rs to their own, separate crate, which does not require a build script. (This is also a common pattern with procedural macros.) Your new project structure might look like
/
|- Cargo.toml
|- build.rs
|- src/
| |- lib.rs
| \- main.rs
|- pump_log_tool_types/
| |- Cargo.toml
| \- src/lib.rs
Then, in /Cargo.toml, create a workspace section and add the new crate:
[workspace]
# This is a path relative to the workspace root.
members = ["pump_log_tool_types"]
In pump_log_tool/Cargo.toml, add the pump_log_tool_types crate as both a normal dependency and a build dependency:
[dependencies]
pump_log_tool_types = { path = "pump_log_tool_types" }
[build-dependencies]
pump_log_tool_types = { path = "pump_log_tool_types" }
Now when you build pump_log_tool, the pump_log_tool_types crate will be built once and shared between the build script and the crate itself.

Related

How do I refer to code in main.rs from tests? [duplicate]

When creating a project with a test like so:
cargo init --bin projectname
mkdir projectname/tests
echo "extern crate projectname;" > projectname/tests/test.rs
cd projectname/
cargo build
I get this error when testing:
cargo test
Compiling projectname v0.1.0 (file:///home/username/Lab/projectname)
error[E0463]: can't find crate for `projectname`
--> tests/test.rs:1:1
|
1 | extern crate projectname;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ can't find crate
How can I access the functions in ´projectname/src/main.rs´ from projectname/tests/test.rs?
How can I access functions in ´projectname/src/main.rs´ from projectname/tests/test.rs?
You cannot.
A binary cannot be used a an external crate (the same way as you can't use a ELF binary as a shared object/library)
You just have to change your initialisation to
cargo init --lib projectname
or rename your main.rs to lib.rs
If you really want to stick with a main, you may look at Rust package with both a library and a binary?.

Cannot import modules in lib.rs [duplicate]

This question already has answers here:
Rust modules confusion when there is main.rs and lib.rs
(4 answers)
Closed 1 year ago.
I'm creating an application in Rust. The problem is that I cannot import a module inside lib.rs file and I wonder how am I supposed to do that.
My folder structure looks like this:
src/
├─ lib.rs
├─ main.rs
├─ app.rs
Inside main.rs file I use mod lib to use the library's public functions. But inside lib.rs file I would like to get the public methods from app.rs file, but unlike in other files, I cannot because of a compiler error:
file not found for module `app`
help: to create the module `app`, create file "src\lib\app.rs" or "src\lib\app\mod.rs"rustc(E0583)
Strangely enough, when I move the app.rs file to newly created lib/app.rs, the compiler would like to find the file in the previous location, which is src/app.rs, nor src/lib/app.rs. It gives me a headache.
Thank you for your answers.
Do not write mod lib;, with that name in particular. While this is technically valid code, it results in your code being compiled twice, under different configurations:
cargo sees lib.rs, and tells rustc to compile a library with that as the “crate root”.
Under this condition, mod app; looks for a sibling file and succeeds.
cargo sees main.rs, and tells rustc to compile a binary with that as the crate root.
rustc reads main.rs and sees mod lib;, so it compiles lib.rs again as a module inside of the binary.
Then, submodules of modules are expected to be in subdirectories, which is why it's asking for src/lib/app.rs.
If you are intending to define a library crate (which can be depended on by other packages, or your own integration tests and examples separate from main), then instead of mod lib;, just write paths referring to the library: use foo::app::some_fn_in_app_rs;, where foo is whatever name is specified in your Cargo.toml's [package] section, because that's the name of the library. The library is automatically made available to the binary.
On the other hand, if you don't have any need for a library, then you can just declare modules starting from main. In that case, don't name any of the modules lib. Any name that's not lib or main is fine.
In either case, you can always move the code around later if your needs change. The only thing that is problematic is when you name a module one of the filenames that means something specific to Cargo.

Refactoring to workspace structure causes extern crate imports to not work

I need different parts of my project to use different versions of the same extern crate so I'm refactoring my Rust project to be divided into multiple packages via the workspaces system using this as a guide. Doing so is causing all my pub extern crate imports to not work.
This post is very similar to one I created very recently and then deleted - this version contains a minimal, complete, and verifiable example.
Here's my project structure
workspace_test/
root/
src/
main.rs
Cargo.toml
Cargo.toml
workspace_test/Cargo.toml:
[package]
name = "workspace_test"
version = "0.1.0"
authors = ["Phoenix <kahlo.phoenix#gmail.com>"]
[workspace]
members = [
"root"
]
[[bin]]
name = "root"
path = "root/src/main.rs"
workspace_test/root/Cargo.toml:
[package]
name = "root"
version = "0.1.0"
authors = ["Phoenix <kahlo.phoenix#gmail.com>"]
[dependencies]
time = "0.1"
workspace_test/root/src/main.rs:
pub extern crate time;
fn main() {
println!("Hello, world!");
}
This is also on github, so it can easily be cloned and cargo run'd.
This is the error:
error[E0463]: can't find crate for `time`
--> root/src/main.rs:1:1
|
1 | pub extern crate time;
| ^^^^^^^^^^^^^^^^^^^^^^ can't find crate
error: aborting due to previous error
error: Could not compile `workspace_test`.
In workspace_test/Cargo.toml you create a package with the binary root. If you execute cargo run, it runs the main.rs, but since you didn't state the dependencies in this manifest file, the error occurs. The dependency is only specified in workspace_test/root/Cargo.toml, which is not used at this point.
I assume you want to use the workspaces proposed by the RFC. You can create a workspace with virtual manifests, which must neither specify a [package] nor [[bin]], so just remove them. workspace_test/Cargo.toml now looks like this:
[workspace]
members = [
"root"
]
If you only have one executable, you can now pass the package: -p/--package
cargo run -p root
or specify the manifest path manually:
cargo run --manifest-path root/Cargo.toml
If root/Cargo.toml contains multiple targets, you can just append the --lib or --bin flags as usual. E.g. this would execute the abc-binary specified in workspace_test/root/Cargo.toml:
cargo run -p root --bin abc

How do you include all crates in a workspace inside the test directory?

I have a binary Rust project which uses the workspaces to manage sub-crates.
Directory structure
/myapp
Cargo.toml
/src
/tests
test.rs
/crates
/printer
Cargo.toml
/src
myapp/Cargo.toml
[package]
name = "myapp"
[workspace]
members = ["crates/printer"]
Inside of test.rs I can compile extern crate myapp; to pull in the parts of the application that are exposed in src/lib.rs. This works as expected.
However, when attempting to compile extern crate printer; it errors that it cannot find it. I've confirmed that the printer package is correctly placed in the top-level Cargo.lock file.
How do I include my sub-crates into the /tests directory at the top level?
There's nothing special about workspaces or even the concept of tests. If you want to use a crate in Rust code, you have to add it as a dependency:
[dependencies]
printer = { path = "crates/printer" }
See also:
How to define test-only dependencies?

How to link main.rs to lib.rs dynamically?

I have a crate with both src/lib.rs and src/main.rs.
main.rs is just using extern crate programname (which is lib.rs) and uses certain functions from lib.rs and it's submodules.
The documentation on linking says:
Pure-Rust dependencies are statically linked by default so you can use created binaries and libraries without installing Rust everywhere.
How can I change this behavior so a binary created from main.rs will be dynamically linked to library produced by lib.rs?
I've added the following to Cargo.toml
[lib]
path = "src/lib.rs"
crate-type = ["dylib"]
[[bin]]
name = "programname"
path = "src/main.rs"
But it does not compile and gives me errors like:
error: cannot satisfy dependencies so `std` only shows up once
help: having upstream crates all available in one format will likely make this go away
If I add "rlib" to lib section, it compiles, but the binary is not linked against libprogramname.so

Resources