Is it possible to avoid recompiling a crate when I haven't made any changes to it? - rust

I have a Rust crate which is a wrapper for a large C API and takes several minutes to compile. Running cargo build in the directory without making any changes always results in a recompile. It seems that Cargo should not be recompiling this crate unless I make a change, which I have not done.
I would like to compile the crate once and avoid re-compiling the crate unless I make a change. Is there any way for me to avoid constantly recompiling this scrate?
It seems that something is likely incorrect in my crate's build script. I will try to create a minimal reproducible example, but in the meantime I have provided the build script below:
use std::env;
use std::fs::copy;
use std::path::Path;
use std::process::Command;
fn main() {
let out_dir = env::var("OUT_DIR").unwrap();
let c_src_path = Path::new("parasail_c");
// configure the build
Command::new("cmake")
.arg(".")
.current_dir(&c_src_path)
.output()
.expect("Failed to configure parasail.");
// build the library
Command::new("make")
.current_dir(&c_src_path)
.output()
.expect("Failed to build parasail.");
// put the static library in the right directory so we can clean up
let target_file = format!("{}/libparasail.so", out_dir);
copy("parasail_c/libparasail.so", target_file)
.expect("Problem copying library to target directoy.");
let target_file = format!("{}/parasail.h", out_dir);
copy("parasail_c/parasail.h", target_file)
.expect("Problem copying header to target directoy.");
// clean up the temporary build files
Command::new("make")
.current_dir(&c_src_path)
.arg("clean")
.output()
.expect("Failed to clean up build files.");
// clean up the configuration files
Command::new("make")
.arg("distclean")
.current_dir(&c_src_path)
.output()
.expect("Failed to clean up configuration files.");
// let cargo know that it can find the file in the out directory
println!("cargo:rustc-link-search=native={}", out_dir);
println!("cargo:rustc-link-lib=dylib=parasail");
}
Here is the output from cargo build --verbose
cargo build --verbose
Compiling parasail-sys v0.1.0 (/home/fortier/testcode/rust/pairhmm/parasail-sys)
Fresh libc v0.2.51
Running `/home/fortier/testcode/rust/pairhmm/parasail-sys/target/debug/build/parasail-sys-f2d2d1f27a70b4d4/build-script-build`
Running `rustc --edition=2018 --crate-name parasail_sys src/lib.rs --color always --crate-type lib --emit=dep-info,link -C debuginfo=2 -C metadata=8879665b3d9bf7e1 -C extra-filename=-8879665b3d9bf7e1 --out-dir /home/fortier/testcode/rust/pairhmm/parasail-sys/target/debug/deps -C incremental=/home/fortier/testcode/rust/pairhmm/parasail-sys/target/debug/incremental -L dependency=/home/fortier/testcode/rust/pairhmm/parasail-sys/target/debug/deps --extern libc=/home/fortier/testcode/rust/pairhmm/parasail-sys/target/debug/deps/liblibc-bc949bf21f4fe772.rlib -L native=/home/fortier/testcode/rust/pairhmm/parasail-sys/target/debug/build/parasail-sys-2ac393455c1f3545/out -l dylib=parasail`
Finished dev [unoptimized + debuginfo] target(s) in 1m 58s
After further inspection I have discovered that the issue is somewhere in the C code that the sub-crate is wrapping. I replaced the current C code with an older version, while changing none of the Rust code and the issue has disappeared. I'll continue doing some further investigation to see exactly what was causing the problem and I'll update this post once I narrow it down.

Add to the build script println!("cargo:rerun-if-changed={}", &file); lines for each file and for each directory.

Related

How to get the binary output of cargo run <rust.rs>?

When we compile a c file using gcc test.c -o test.
We can get the binary file as test.
But while running a file using cargo run test.rs in rust.
can we get the binary like we got in the C program?
The original hello.c file:
void main() {
// printf() displays the string inside quotation
printf("Hello, World!");
}
The rust program:
extern "C" {
fn printf(_: *const libc::c_char, _: ...) -> libc::c_int;
}
unsafe fn main_0() {
// printf() displays the string inside quotation
printf(b"Hello, World!\x00" as *const u8 as *const libc::c_char);
}
pub fn main() { unsafe { main_0() } ::std::process::exit(0i32); }
When using cargo it compiles and runs perfectly.
└─$ cargo run hello.rs
Compiling Rust_testing v0.1.0 (/home/pegasus/Documents/Rust_testing)
warning: crate `Rust_testing` should have a snake case name
|
= note: `#[warn(non_snake_case)]` on by default
= help: convert the identifier to snake case: `rust_testing`
warning: `Rust_testing` (bin "Rust_testing") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/Rust_testing hello.rs`
Hello, world!
Here's my Cargo.toml file:
[package]
name = "Rust_testing"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libc = "0.2"
I have a rust program named hello.rs.
The program is I'm unable to compile it using rustc. I generated the hello.rs using c2rust online transpiler. But if I use cargo run hello.rs the program runs smoothly.
while using rustc new.rs -o test,
I can get the x86 test binary.
How to get similar kind of file while using the cargo run new.rs?
I looked into the target/debug directory.
But there are so many directories and so many files there. How to know which on is created for which .rs file?
┌──(pegasus㉿pegasus)-[~/Documents/Rust_testing/target/debug]
└─$ ls
build deps examples incremental Rust_testing Rust_testing.d
If you do cargo build, you will find the binary in target/debug/. If you build in release via cargo build --release, you will find it in target/release/.
Be aware that cargo run hello.rs does not compile hello.rs. It will always compile src/main.rs. hello.rs will be passed to the compiled program as a command line argument.
How to know which on is created for which .rs file?
There isn't one file for one .rs file. If your crate is a binary crate, then there will be exactly one executable with the name of your crate. In your case it's Rust_testing. You can run it with ./target/debug/Rust_testing, or copy it somewhere else and execute it directly.
You can add multiple binaries per crate by putting them in the src/bin folder. For example, if you put your hello.rs file in src/bin and then execute cargo build --all, it will create a target/debug/hello executable that you can run.
For more information about cargo's folder layout, read the cargo documentation.
If you are new to Rust, I highly recommend reading the Rust book. It will guide you through how to use rustup, rustc and cargo step by step.

Is it possible to run a single rust file as a script while linking to other libraries without generating cargo projects

I'd like to run a one off rust "script" without going through creating a cargo project for a single run (since I am providing this script to colleagues).
Ideally I could build directly with the command line avoiding creating cargo projects etc.
for instance:
use serde_json::Value;
use some_private_packege_i_own_locally_in_another_directory;
fn main() {
// do some stuff with these packages and die
}
I would need to depend on the serde_json and my some_private_packege_i_own_locally_in_another_directory.
(A bit similar to rust playground I suppose for a single time use)
Something similar to this from the command line would be great:
rustc /path/to/main.rs --dependency serde_json, my_package ...
You can specify a dependency with with extern flag, and you can specify the location of transitive dependencies, with -L dependency. You will have to compile each dependency, and all of it's dependencies manually:
// compile all of serde's dependencies
// compile all of hyper's dependencies
// compile serde
// compile hyper
rustc script.rs --crate-type bin -L dependency=~/tmp/deps --extern serde_json=~/tmp/deps/serde_json.rlib --extern hyper=~/tmp/deps/hyper.rlib
As you can tell, this would get very difficult, even with two direct dependencies. Instead, you can use cargo-script, which handles all of this for you:
cargo install cargo-script
cargo script -D hyper -D serde_json script.rs

Could not able to release a file. getting error: could not compile 'libc'

I am very new to this language and coding field. Beginner to coding field as well.
I tried to build and release file but getting an error Compiling libc v0.2.62
error: Could not compile `libc`
pi#raspberrypi:~/Ganesh_Rust/Real_time/led_blink/src $ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Running `/home/pi/Ganesh_Rust/Real_time/led_blink/target/debug/led_blink`
pi#raspberrypi:~/Ganesh_Rust/Real_time/led_blink/src $ cargo build --release
Compiling libc v0.2.62
error: Could not compile `libc`.
Caused by:
process didn't exit successfully: `rustc --crate-name build_script_build /home/pi/.cargo/registry/src/github.com-1ecc6299db9ec823/libc-0.2.62/build.rs --color always --crate-type bin --emit=dep-info,link -C opt-level=3 --cfg 'feature="default"' --cfg 'feature="std"' -C metadata=b79e3ef31fa8c249 -C extra-filename=-b79e3ef31fa8c249 --out-dir /home/pi/Ganesh_Rust/Real_time/led_blink/target/release/build/libc-b79e3ef31fa8c249 -L dependency=/home/pi/Ganesh_Rust/Real_time/led_blink/target/release/deps --cap-lints allow` (signal: 11, SIGSEGV: invalid memory reference)
code: this program which i wrote in VS code to blink LED on raspberry pi 3
use rust_gpiozero::*;
use std::thread;
use std::time::Duration;
fn main() {
//create a new LEd attached to pin 17 of raspberry pi
let led = LED::new(17);
//blink the led 5 times
for _ in 0.. 5{
led.on();
thread::sleep(Duration::from_secs(10));
led.off();
thread::sleep(Duration::from_secs(10));
}
}
cargo.toml file:
[package]
name = "led_blink"
version = "0.1.0"
authors = ["pi"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rust_gpiozero = "0.2.0"
I am getting output on Raspberry pi but executable file and binary files are large (5MB). So i thought if i do release maybe i can reduce size so tried to release using cargo build --release command but getting this error.
If you're using rustup-provided binaries, then this is a known issue upstream. There is a workaround in that issue, which is to set the following in Cargo.toml:
[profile.release]
codegen-units = 1
As an alternative, you can use the Debian rustc and cargo packages instead of rustup, which should work just fine. You can either download the packages from https://packages.debian.org/rustc and https://packages.debian.org/cargo, or you can add an appropriate APT line into /etc/sources.list (see https://deb.debian.org/ for an example). Note that Debian does not always have the latest version, but they should work.

How can you compile a Rust library to target asm.js?

I've got a Rust library with the following usual structure:
Cargo.toml
src
|--lib.rs
.cargo
|--config (specifies target=asmjs-unknown-emscripten)
target
|......
When I do cargo build, I get a new directory under target called asmjs-unknown-emscripten, but the .js files that I'd expect are not there.
As this user notes, you've got to do something special to export functions to asm.js besides marking them public:
Basically you have this boilerplate right now:
#[link_args = "-s EXPORTED_FUNCTIONS=['_hello_world']"]
extern {}
fn main() {}
#[no_mangle]
pub extern fn hello_world(n: c_int) -> c_int {
n + 1
}
Then you can use this in your javascript to access and call the function:
var hello_world = cwrap('hello_world', 'number', ['number']);
console.log(hello_world(41));
However, Rust complains about the #[link_args...] directive as deprecated. Is there any documentation out there that can explain how this works?
Very interesting question! I was running into similar dependency issues with fable.
I have checked Compiling Rust to your Browser - Call from JavaScript, Advanced Linking - Link args and How to pass cargo linker args however was not able to use cargo in the same way as rustc --target asmjs-unknown-emscripten call-into-lib.rs.
The closer I was able to get was to run both cargo and rustc like
cd lib1
cargo build --target asmjs-unknown-emscripten
rustc --target=asmjs-unknown-emscripten src\lib.rs
cd ..
cd lib2
cargo build --target asmjs-unknown-emscripten
rustc --target=asmjs-unknown-emscripten src\lib.rs --extern lib1=..\lib1\target\asmjs-unknown-emscripten\debug\liblib1.rlib
cd ..
cd lib3
cargo build --target asmjs-unknown-emscripten
rem rustc --target=asmjs-unknown-emscripten src\lib.rs --extern webplatform=..\lib3\target\asmjs-unknown-emscripten\debug\deps\libwebplatform-80d107ece17b262d.rlib
rem the line above fails with "error[E0460]: found possibly newer version of crate `libc` which `webplatform` depends on"
cd ..
cd app
cargo build --target asmjs-unknown-emscripten
cd ..
see the so-41492672-rust-js-structure. It allows to have several libraries that compile together to the JavaScript in the final application.
I still think some manual linking would help. Would be interested to know.
P.S. to see what rustc uses to link, you can pass -Z print-link-args to it.

Is there a simpler way to run clippy on my build script?

In a Cargo project, I can easily run clippy on my src code using this command:
rustup run nightly cargo clippy
However, if I'm using a build script, I'd like to run clippy on that as well. For instance, if my build.rs file looks like this:
fn main() {
let foo = "Hello, world!";
println!("{}", foo);
}
I'd like to see this when I run clippy:
warning: use of a blacklisted/placeholder name `foo`, #[warn(blacklisted_name)] on by default
--> build.rs:2:9
|
2 | let foo = "Hello, world!";
| ^^^
|
= help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#blacklisted_name
The only way I can think of to run clippy on my build script is to copy it into a cargo new temporary project, run clippy, make my changes there, and copy back, but this is horribly inconvenient and quickly becomes infeasible when build dependencies and the like are added to the mix.
Is there a simpler way to analyze my build script with clippy?
Note: This solution no longer works. The clippy plugin feature has been removed (source).
There are two ways to use Clippy: the cargo clippy command and the clippy compiler plugin. cargo clippy detects the build script as a dependency of the main project, so it doesn't load the compiler plugin.
Therefore, the other option is to use the compiler plugin directly. The instructions for doing this are in clippy's README. We need to make a few adaptations for using it on the build script, though.
First, we need to add clippy as a build dependency:
[build-dependencies]
clippy = { version = "*", optional = true }
[features]
default = []
Adding it to [dependencies] instead will not work (the result is error[E0463]: can't find crate for `clippy`), as Cargo will not pass the path to dependencies to the compiler when building the build script.
Then, we need to add this at the top of build.rs:
#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
Finally, we need to build with the clippy feature enabled:
$ cargo build --features clippy
If you want to run clippy on both the build script and on the main project when you use the command above, add the same clippy dependency to [dependencies], then add the cfg_attr attributes to the crate root(s) (lib.rs, main.rs, etc.).

Resources