How to compile rust packages with different targets - rust

Lets say I have nested packages like /foo/bar
Now I want to compile these packages with different targets.
So, How to compile /foo package with target wasm32-unknown-unknownand /foo/bar package with target x86_64-unknown-linux-gnu with one build command like cargo build --release?

You can specify the default target (that will be used absent an explicit --target option on the command line) in a configuration file. I therefore suggest that you create the following files:
/foo/.cargo/config.toml
[build]
target = "wasm32-unknown-unknown"
/foo/bar/.cargo/config.toml
[build]
target = "x86_64-unknown-linux-gnu"
To ensure that /foo/bar is built whenever /foo is built, you can use a build script:
/foo/build.rs
fn main() {
println!("cargo:rerun-if-changed=bar");
let profile = std::env::var("PROFILE").unwrap();
let status = std::process::Command::new("cargo")
.arg("build")
.arg(format!("--{}", profile))
.current_dir("bar")
.status()
.expect("failed to execute cargo");
assert!(status.success(), "failed to build bar");
}

Related

Depend on the binary of another workspace package being built by cargo

I have a workspace project with multiple packages. The two important ones are:
flowc - which is a lib and a binary
flowstdlib
flowc is a kind of compiler that I build as part of the project.
flowstdlib has a build script, that uses the flowc built binary to build that package (generate "code", files etc), so I need the flowc compiler ready when flowstdlib is to be built.
In cargo.toml of flowstdlib I define flowc as a build dependency:
[build-dependencies]
flowc = {path = "../flowc", version = "0.31.0" }`
(I've tried making it also a dependency, but no change)
in the build.rs of flowstdlib I look for it in the path, and if not found in the ../target/debug/flowc location:
let flowc = if Path::new(env!("CARGO_MANIFEST_DIR")).join("../target/debug/flowc").exists() {
"../target/debug/flowc"
} else if Simpath::new("PATH").find_type("flowc", FileType::File).is_ok() {
"flowc"
} else {
""
};
When I run the build, it looks like it's trying to build multiple packages at the same time in parallel:
Compiling flowstdlib v0.31.0 (/Users/andrew/workspace/flow/flowstdlib)
Compiling flowsamples v0.31.1 (/Users/andrew/workspace/flow/samples)
warning: Could not find `flowc` in $PATH or `target/debug`, so cannot build flowstdlib
error: failed to run custom build command for `flowsamples v0.31.1 (/Users/andrew/workspace/flow/samples)`
and the flowstdlib build fails as flowc binary is not built yet.
Since the build continues and eventually finishes building flowc, if I re-run the build, it will work the second time around (as flowc binary is now found).
So:
it looks like a build-dependency does wait for the depended-on binary to be built (maybe it waits for the library to be built, hard to tell)
Question
How I can make the build of flowstdlib wait for the completion of the flowc binary?
(without forcing a non-parallel build)
Pending the RFC landing, my workaround is to split the build into two commands (I'm using a Makefile to invoke them currently):
* cargo build -p flowc # will complete the build of the flowc binary
* cargo build # will build the entire workspace, including flowstdlib

How to list a project's source files using the cargo crate?

I'm trying to list the source files of a Rust project using the cargo crate. I can not just simply list all the .rs files present in a directory as I want to retrieve exactly the files that the compiler sees during the compilation, which may not be all the .rs files.
I'm conducting my experiments on the the Alacritty repository, which has a cargo workspace of 3 projects. Here is my code so far:
extern crate cargo;
use std::path::Path;
use cargo::core::Source;
fn main() {
let path = Path::new("/tmp/alacritty/Cargo.toml");
let config = cargo::util::config::Config::default().unwrap();
let ws = cargo::core::Workspace::new(&path, &config).unwrap();
for pkg in ws.members() {
println!("found package {}", pkg);
let config = ws.config();
let mut src = cargo::sources::PathSource::new(pkg.root(), pkg.package_id().source_id(), config);
src.update().unwrap();
let src_files = src.list_files(pkg).unwrap();
println!("found {} source files", src_files.len());
}
}
Here is the output:
found package alacritty v0.5.0-dev (/tmp/alacritty/alacritty)
found 0 source files
found package alacritty_terminal v0.5.0-dev (/tmp/alacritty/alacritty_terminal)
found 0 source files
found package font v0.1.0 (/tmp/alacritty/font)
found 0 source files
The members of the workspace are correctly found but I fail to retrieve the source files for each of these members. What am I missing?
Your code works!
If you run 'cargo vendor' in the alacritty tree, this should solve your issue. Study the 'cargo vendor' command Also, study the --offline switch for the cargo build command. I did not need to use this, but it is very helpful reading.
Basically, cargo vendor pulls in all the source.
I am not sure exactly why your code is not working. I had difficulty recreating this using the /tmp directory. I then used a normal directory combined with a call to 'cargo vendor', and it worked. Before cutting and pasting my code below, be sure to change '/Users/[username]' with your own path to your home directory.
Here is my procedure:
cd ~
git clone https://github.com/jwilm/alacritty
cargo vendor
This next part is probably not necessary:
mkdir /Users/[username]/alacritty/.cargo
Create a file at /Users/[username]/alacritty/.cargo/config
and, insert the following:
[source.crates-io]
replace-with = "vendored-sources"
[source.vendored-sources]
directory = "vendor"
Continuation of necessary part:
Modify the path statement to point to the newly created alacritty path:
let path = Path::new("/Users/[username]/alacritty/Cargo.toml");
Now, run your code
cargo run
Here is my output:
cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/test3`
found package alacritty v0.5.0-dev (/Users/jmurray/alacritty/alacritty)
found 18 source files
found package alacritty_terminal v0.5.0-dev
(/Users/[username]/alacritty/alacritty_terminal)
found 172 source files
found package font v0.1.0 (/Users/jmurray/alacritty/font)
found 12 source files
Search for each element of the needle in the haystack in order. Each time you find a matching element, only continue the search in the remaining portion of the haystack. You can express this nicely by taking a new subslice of of the haystack each time you match an element.

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

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.

How can I set default build target for Cargo?

I tried to make 'Hello World' in Rust using this tutorial, but the build command is a bit verbose:
cargo +nightly build --target wasm32-unknown-unknown --release
Is it possible to set the default target for cargo build?
You could use a Cargo configuration file to specify a default target-triple for your project. In your project's root, create a .cargo directory and a config.toml file in it with the following contents:
[build]
target = "wasm32-unknown-unknown"
As listed in the Cargo documentation, you can create a .cargo/config and specify the target:
[build]
target = "my-custom-target"
If you're able to use unstable features, following documentation and especially per-package-target feature add this to you crate manifest that usually named Cargo.toml
cargo-features = ["per-package-target"]
[package]
forced-target = "wasm32-unknown-unknown"
# and/or:
default-target = "wasm32-unknown-unknown"
This requires nightly toolchain.

How to specify the path to a Cargo.toml

I'm new to Rust and I want to build and run my project. I use something like:
cd %project_path%
cargo run
I want to be able to write cargo run -path %project_path% in a single line because I want to create a build script that doesn't allow changing the working directory. It seems that cargo doesn't have any -path or -target keys, which would define target directory, and I always get the message
could not find Cargo.toml in C:\WINDOWS\system32 or any parent
directory
The --manifest-path path/to/Cargo.toml option to almost all cargo subcommands allows pointing it to a specific Cargo.toml file to use, overriding the default of searching the current directory and its parents for a file called Cargo.toml (this file is the "manifest").
Incidentally, unix-y commands usually take a -h or --help argument which prints information about their command line options, cargo and rustc are no exception. E.g.
$ cargo run --help
Run the main binary of the local package (src/main.rs)
Usage:
cargo run [options] [--] [<args>...]
Options:
-h, --help Print this message
--bin NAME Name of the bin target to run
--example NAME Name of the example target to run
-j N, --jobs N The number of jobs to run in parallel
--release Build artifacts in release mode, with optimizations
--features FEATURES Space-separated list of features to also build
--no-default-features Do not build the `default` feature
--target TRIPLE Build for the target triple
--manifest-path PATH Path to the manifest to execute
-v, --verbose Use verbose output
-q, --quiet No output printed to stdout
--color WHEN Coloring: auto, always, never
If neither `--bin` nor `--example` are given, then if the project only has one
bin target it will be run. Otherwise `--bin` specifies the bin target to run,
and `--example` specifies the example target to run. At most one of `--bin` or
`--example` can be provided.
All of the trailing arguments are passed to the binary to run. If you're passing
arguments to both Cargo and the binary, the ones after `--` go to the binary,

Resources