How do I set environment variables with Cargo? - rust

I have a dependency listed in Cargo.toml that needs a specific environment variable set. I can run export FOO=bar in bash and all works well, but for the life of me I can't figure out how to export this environment variable at compile time with Cargo. I've tried setting the environment variable in build.rs via std::env, Command, and println!, all to no effect:
// build.rs
fn main() {
Command::new("ls")
.env("FOO", "bar")
.spawn()
.expect("ls command failed to start");
}
// build.rs
fn main() {
std::env::set_var("FOO", "bar");
}
// build.rs
fn main() {
println!("cargo:rustc-env=FOO=bar");
}

Since Cargo 1.56, you can use the configurable-env feature via the [env] section in config.toml. This is not the same file as Cargo.toml, but it can still be set per project:
[env]
FOO = "bar"
PATH_TO_SOME_TOOL = { value = "bin/tool", relative = true }
USERNAME = { value = "test_user", force = true }
Environment variables set in this section will be applied to the environment of any processes executed by Cargo.
relative means the variable represents a path relative to the location of the directory that contains the .cargo/ directory that contains the config.toml file.
force means the variable can override an existing environment variable.
For more information about the history of this feature, see the related GitHub issue.

Related

Patch.crates-io is not applied

I am using serde-xml-rs crate in our project and I need to modify some code from that crate, so what I did is to generate a patch file, and apply it by calling cargo patch. In my Cargo.toml has stuff like following:
serde-xml-rs = "0.6.0"
[package.metadata.patch.serde-xml-rs]
version = "0.6.0"
patches = [
"patches/0001.patch"
]
[patch.crates-io]
serde-xml-rs = { path = "./target/patch/serde-xml-rs-0.6.0" }
When I called cargo run, project still use original version of serde-xml-rs instead of using values in patch.crates-io tag
But I could see that my patch is applied correctly to the target folder "./target/patch/serde-xml-rs-0.6.0", because if I modify toml file to following, it worked.
{ path = "./target/patch/serde-xml-rs-0.6.0" }

How to create a target specific profile in Cargo.toml?

I would like to specify a separate [profile.release] for my library when cfg(target_os = "ios") is active. I tried this:
[profile.'cfg(not(target_os = "ios"))'.release]
lto = true # Use link-time optimization.
[profile.'cfg(target_os = "ios")'.release]
lto = true # Use link-time optimization.
opt-level = "z" # Mobile devices are fast enough. Optimize for size instead.
strip = "symbols" # Not relevant for windows.
However when I now try to build my project / workspace I get the following error:
error: failed to parse manifest at `/path/to/Cargo.toml`
Caused by:
invalid character `(` in profile name `cfg(not(target_os = "ios"))`
Allowed characters are letters, numbers, underscore, and hyphen.
which is to be expected, because according to the documentation only [profile.<name>] is allowed.
Is there any method to achieve the desired behaviour?
P.S. The full target name would be aarch64-apple-ios in case this is needed.
This was requested in issue #4897, Per-target profiles?, but not yet implemented.
In the meantime, you can use a script that checks the target and set environment variables to override config (for example, set CARGO_PROFILE_RELEASE_LTO or CARGO_PROFILE_RELEASE_OPT_LEVEL) then invokes Cargo with them.
Here is an example, from users.rust-lang.org - How to modify profile.release only for a target?
Create a binary in the workspace named e.g. custom_build. In custom_build/src/main.rs put:
use std::env;
use std::process::Command;
fn main() {
let mut cargo = Command::new(env::var_os("CARGO").unwrap());
cargo.env("CARGO_CUSTOM_BUILD", "1"); // So we can disallow regular builds, to prevent mistakes
cargo.args(env::args_os().skip(1));
// You can determine the target OS by various way, but providing it manually to the build script is the simplest.
let for_ios = env::var("build_ios").is_ok();
if for_ios {
cargo.env("CARGO_PROFILE_RELEASE_LTO", "true");
cargo.env("CARGO_PROFILE_RELEASE_OPT_LEVEL", "z");
cargo.env("CARGO_PROFILE_RELEASE_STRIP", "symbols");
} else {
cargo.env("CARGO_PROFILE_RELEASE_LTO", "true");
}
let cargo_succeeded = cargo.status().ok().map_or(false, |status| status.success());
if !cargo_succeeded {
std::process::exit(1);
}
}
Then you can create a build.rs file to prevent manually running cargo:
use std::env;
fn main() {
if !env::var("CARGO_CUSTOM_BUILD").ok().map_or(false, |s| s == "1") {
panic!("Do not run `cargo ...`, run `cargo custom_build ...` instead")
}
}

Unable to generate .so file for solana deployment. (No errors)

I am trying to understand the solana/example-helloworld by re-writing the rust lib myself. I have done this and from the package.json file, to generate the .so file, following is what is run:
cargo build-bpf --manifest-path=./src/program-rust/Cargo.toml --bpf-out-dir=dist/program
I used above since I already have cargo setup locally and run above against my setup, I faced a few issues, from the edition (was using 2021, had to downgrade to 2018) to having to rename main.rs to lib.rs and others I can't remember.
Below is my actual running command from the terminal in the project root directory where the Cargo.toml file is in:
cargo build-bpf --manifest-path=./Cargo.toml --bpf-out-dir=dist/program
But upon running above, in the dist directory, there is nothing
This is actually a multi-tied question:
.so file are signifies solana files, right?
What do we mean by cargo build-bpf?
Is there any reason, why 2021 edition didn't work for the solana example?
Finally, why does the above command not output my .so file?
My Cargo.toml below:
[package]
name = "jjoek-sc"
version = "0.0.1"
description = "My first rust program for solana (hello john)"
authors = ["JJoek <info#jjoek.com>"]
license = "Apache-2.0"
homepage = "https://jjoek.com/"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
borsh = "0.9.1"
borsh-derive = "0.9.1"
solana-program = "~1.8.14"
Below is my lib.rs
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
pubkey::Pubkey,
msg,
program_error::ProgramError,
};
/// Define the type of state stored in accounts
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
/// number of greetings
pub counter: u32,
}
// Declare and export the program's entrypoint
entrypoint!(process_instruction);
// Program entrypoint's implementation
pub fn process_instruction(program_id: &Pubkey, accounts: &[AccountInfo], _instruction_data: &[u8]) -> ProgramResult {
msg!("Hello, John everything is gonna be okay");
msg!("Hello World Rust program entrypoint");
// Iterating accounts is safer than indexing
let accounts_iter = &mut accounts.iter();
// Get the account to say hello to
let account = next_account_info(accounts_iter)?;
// The account must be owned by the program in order to modify its data
if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
// Increment and store the number of times the account has been greeted
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
msg!("Greeted {} time(s)!", greeting_account.counter);
Ok(())
}
.so file are signifies solana files, right?
so stand for shared object, also known as a dynamically relocatable library or dylib in Cargo.toml.
What do we mean by cargo build-bpf?
BPF is a virtual machine inside the kernel, so this should instruct cargo to build for that target.
Is there any reason, why 2021 edition didn't work for the solana example?
I don't know, but I suspect it's a simple fix.
Finally, why does the above command not output my .so file?
Could it be that you are missing the lib section in Cargo.toml:
[lib]
crate-type = ["cdylib", "lib"]

Cargo activates features by itself

I have a binary that depends on a library. In the library I specified these features:
[features]
default = []
fet = []
I would expect feature fet would ONLY be activated when it is compiled with the feature flag. In my binary the Cargo.toml looks like this:
[dependencies]
a = { path = "./a" }
[features]
default = []
I haven't specified the fet feature in the Cargo.toml, however this gives me a compilation error in my binary:
fn main() {
if cfg!(feature = "fet") {
compile_error!("not expected");
}
}
Why is feature fet still activated in my binary? I executed these commands and get the same error:
cargo run
cargo run --features=default
Ideally, I want my binary to activate certain features from dependencies if they are explicitly mentioned in the cargo run command (in the --features flag). Is this possible? I was hoping this would work in my binary Cargo.toml:
[features]
default = []
fet = ["a/fet"]
and feature fet would be activated if I ran this command:
cargo run --features=fet
The cfg! macro will be expanded to true or false depending on if the condition is true or false, so if the feature flag is not set, it will still expand to this:
fn main() {
if false {
compile_error!("not expected");
}
}
The compile_error! macro will still be invoked, so you'll get the compiler error either way.
The cfg! macro is mostly useful when you can rely on the compiler optimizing away branches that will never be taken, such as an if false branch. The code still needs to compile before optimization, though, so it's less useful when the code will only compile if the condition is true or false.
What you'd want to use instead is either a #[cfg] attribute or the cfg_if::cfg_if! macro:
// with #[cfg] attribute
fn main() {
#[cfg(feature = "fet")]
compile_error!("not expected");
}
// with cfg_if!
use cfg_if::cfg_if;
fn main() {
cfg_if!{
if #[cfg(feature = "fet")] {
compile_error!("not expected");
}
}
}

Import rust package with alias in toml

I'm trying to make a simple program checking execution times on two different branches of the same rust project.
I wanted to have my .toml look something like this
[dependencies]
cron_original = { git = "https://github.com/zslayton/cron" }
cron_fork = { git = "https://github.com/koenichiwa/cron", branch = "feature/reimplement-queries"}
And my program look something like this:
fn main() {
let expression = String::from("0-59 * 0-23 ?/2 1,2-4 ? *");
let schedule_orig = cron_original::Schedule::from_str(expression);
let schedule_fork = cron_fork::Schedule::from_str(expression);
// Check difference in execution times on these structs
}
but I'm getting no matching package named 'cron_fork' found. Is there anyway to import a package with a specific alias? I was thinking about creating something that would automate checks like this.
You need to specify package keys for those dependencies so cargo knows that you really want those packages even though you specify a different name:
[dependencies]
cron_original = { git = "https://github.com/zslayton/cron", package="cron" }
cron_fork = { git = "https://github.com/koenichiwa/cron", branch = "feature/reimplement-queries", package="cron" }
See the Renaming dependencies in Cargo.toml section in Specifying Dependencies documentation for details.

Resources