Skip to content

Commit

Permalink
Allow build-std to take an array of crate names (#1488)
Browse files Browse the repository at this point in the history
Closes #896.

This is technically a breaking change because arbitrary strings passed
to the `CROSS_BUILD_STD` environment variable (e.g. `yes`) are now no
longer parsed as truthy values, but rather as a single crate name (or
multiple, if containing commas) to pass to `-Zbuild-std` (resulting in
e.g. `-Zbuild-std=yes`, which is invalid).
Behaviour remains the same when `CROSS_BUILD_STD` is set to `true`,
`false`, or a number that fits in an `i32`.
  • Loading branch information
Emilgardis committed May 9, 2024
2 parents 764d258 + 45247c1 commit be13600
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 44 deletions.
6 changes: 6 additions & 0 deletions .changes/1488.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"description": "Allow `build-std` to take an array of crate names",
"issues": [896],
"type": "changed",
"breaking": true
}
8 changes: 4 additions & 4 deletions docs/config_file.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,10 @@ targets:

```toml
[target.aarch64-unknown-linux-gnu]
build-std = false # always build the std library. has precedence over xargo
xargo = false # disable the use of xargo
image = "test-image" # use a different image for the target
runner = "qemu-user" # wrapper to run the binary (must be `qemu-system`, `qemu-user`, or `native`).
build-std = ["core", "alloc"] # always build the `core` and `alloc` crates from the std library. has precedence over xargo
xargo = false # disable the use of xargo
image = "test-image" # use a different image for the target
runner = "qemu-user" # wrapper to run the binary (must be `qemu-system`, `qemu-user`, or `native`).
```


Expand Down
41 changes: 29 additions & 12 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::cross_toml::BuildStd;
use crate::docker::custom::PreBuild;
use crate::docker::{ImagePlatform, PossibleImage};
use crate::shell::MessageInfo;
Expand Down Expand Up @@ -63,8 +64,14 @@ impl Environment {
self.get_values_for("XARGO", target, bool_from_envvar)
}

fn build_std(&self, target: &Target) -> (Option<bool>, Option<bool>) {
self.get_values_for("BUILD_STD", target, bool_from_envvar)
fn build_std(&self, target: &Target) -> (Option<BuildStd>, Option<BuildStd>) {
self.get_values_for("BUILD_STD", target, |v| {
if let Some(value) = try_bool_from_envvar(v) {
BuildStd::Bool(value)
} else {
BuildStd::Crates(v.split(',').map(str::to_owned).collect())
}
})
}

fn zig(&self, target: &Target) -> (Option<bool>, Option<bool>) {
Expand Down Expand Up @@ -189,12 +196,16 @@ fn split_to_cloned_by_ws(string: &str) -> Vec<String> {
/// this takes the value of the environment variable,
/// so you should call `bool_from_envvar(env::var("FOO"))`
pub fn bool_from_envvar(envvar: &str) -> bool {
try_bool_from_envvar(envvar).unwrap_or(!envvar.is_empty())
}

pub fn try_bool_from_envvar(envvar: &str) -> Option<bool> {
if let Ok(value) = bool::from_str(envvar) {
value
Some(value)
} else if let Ok(value) = i32::from_str(envvar) {
value != 0
Some(value != 0)
} else {
!envvar.is_empty()
None
}
}

Expand Down Expand Up @@ -350,8 +361,8 @@ impl Config {
self.bool_from_config(target, Environment::xargo, CrossToml::xargo)
}

pub fn build_std(&self, target: &Target) -> Option<bool> {
self.bool_from_config(target, Environment::build_std, CrossToml::build_std)
pub fn build_std(&self, target: &Target) -> Result<Option<BuildStd>> {
self.get_from_ref(target, Environment::build_std, CrossToml::build_std)
}

pub fn zig(&self, target: &Target) -> Option<bool> {
Expand Down Expand Up @@ -531,7 +542,10 @@ mod tests {

let env = Environment::new(Some(map));
assert_eq!(env.xargo(&target()), (Some(true), None));
assert_eq!(env.build_std(&target()), (Some(false), None));
assert_eq!(
env.build_std(&target()),
(Some(BuildStd::Bool(false)), None)
);
assert_eq!(env.zig(&target()), (None, None));
assert_eq!(env.zig_version(&target()), (None, None));
assert_eq!(env.zig_image(&target())?, (Some("zig:local".into()), None));
Expand Down Expand Up @@ -621,7 +635,7 @@ mod tests {
let env = Environment::new(Some(map));
let config = Config::new_with(Some(toml(TOML_BUILD_XARGO_FALSE)?), env);
assert_eq!(config.xargo(&target()), Some(true));
assert_eq!(config.build_std(&target()), None);
assert_eq!(config.build_std(&target())?, None);
assert_eq!(
config.pre_build(&target())?,
Some(PreBuild::Lines(vec![
Expand All @@ -637,12 +651,15 @@ mod tests {
pub fn env_target_and_toml_target_xargo_target_then_use_env() -> Result<()> {
let mut map = HashMap::new();
map.insert("CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_XARGO", "true");
map.insert("CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_BUILD_STD", "true");
map.insert("CROSS_TARGET_AARCH64_UNKNOWN_LINUX_GNU_BUILD_STD", "core");
let env = Environment::new(Some(map));

let config = Config::new_with(Some(toml(TOML_TARGET_XARGO_FALSE)?), env);
assert_eq!(config.xargo(&target()), Some(true));
assert_eq!(config.build_std(&target()), Some(true));
assert_eq!(
config.build_std(&target())?,
Some(BuildStd::Crates(vec!["core".to_owned()]))
);
assert_eq!(config.pre_build(&target())?, None);

Ok(())
Expand All @@ -656,7 +673,7 @@ mod tests {
let env = Environment::new(Some(map));
let config = Config::new_with(Some(toml(TOML_BUILD_XARGO_FALSE)?), env);
assert_eq!(config.xargo(&target()), Some(true));
assert_eq!(config.build_std(&target()), None);
assert_eq!(config.build_std(&target())?, None);
assert_eq!(config.pre_build(&target())?, None);

Ok(())
Expand Down
50 changes: 39 additions & 11 deletions src/cross_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub struct CrossBuildConfig {
#[serde(default)]
env: CrossEnvConfig,
xargo: Option<bool>,
build_std: Option<bool>,
build_std: Option<BuildStd>,
#[serde(default, deserialize_with = "opt_string_bool_or_struct")]
zig: Option<CrossZigConfig>,
default_target: Option<String>,
Expand All @@ -44,7 +44,7 @@ pub struct CrossBuildConfig {
#[serde(rename_all = "kebab-case")]
pub struct CrossTargetConfig {
xargo: Option<bool>,
build_std: Option<bool>,
build_std: Option<BuildStd>,
#[serde(default, deserialize_with = "opt_string_bool_or_struct")]
zig: Option<CrossZigConfig>,
#[serde(default, deserialize_with = "opt_string_or_struct")]
Expand All @@ -58,6 +58,28 @@ pub struct CrossTargetConfig {
env: CrossEnvConfig,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)]
#[serde(untagged, rename_all = "kebab-case")]
pub enum BuildStd {
Bool(bool),
Crates(Vec<String>),
}

impl Default for BuildStd {
fn default() -> Self {
Self::Bool(false)
}
}

impl BuildStd {
pub fn enabled(&self) -> bool {
match self {
Self::Bool(enabled) => *enabled,
Self::Crates(arr) => !arr.is_empty(),
}
}
}

/// Dockerfile configuration
#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
Expand Down Expand Up @@ -306,8 +328,8 @@ impl CrossToml {
}

/// Returns the `build.build-std` or the `target.{}.build-std` part of `Cross.toml`
pub fn build_std(&self, target: &Target) -> (Option<bool>, Option<bool>) {
self.get_value(target, |b| b.build_std, |t| t.build_std)
pub fn build_std(&self, target: &Target) -> (Option<&BuildStd>, Option<&BuildStd>) {
self.get_ref(target, |b| b.build_std.as_ref(), |t| t.build_std.as_ref())
}

/// Returns the `{}.zig` or `{}.zig.version` part of `Cross.toml`
Expand Down Expand Up @@ -647,7 +669,7 @@ mod tests {
volumes: Some(vec![p!("VOL1_ARG"), p!("VOL2_ARG")]),
},
xargo: Some(false),
build_std: Some(true),
build_std: Some(BuildStd::Bool(true)),
zig: None,
image: Some("test-image".into()),
runner: None,
Expand Down Expand Up @@ -923,7 +945,7 @@ mod tests {
passthrough = ["VAR3"]
[build]
build-std = true
build-std = ["core", "alloc"]
xargo = false
default-target = "aarch64-unknown-linux-gnu"
Expand Down Expand Up @@ -962,7 +984,7 @@ mod tests {
passthrough = ["VAR3"]
[build]
build-std = true
build-std = ["core", "alloc"]
xargo = false
default-target = "aarch64-unknown-linux-gnu"
Expand All @@ -983,7 +1005,13 @@ mod tests {
// need to test individual values. i've broken this down into
// tests on values for better error reporting
let build = &cfg_expected.build;
assert_eq!(build.build_std, Some(true));
assert_eq!(
build.build_std,
Some(BuildStd::Crates(vec![
"core".to_owned(),
"alloc".to_owned()
]))
);
assert_eq!(build.xargo, Some(false));
assert_eq!(build.default_target, Some(p!("aarch64-unknown-linux-gnu")));
assert_eq!(build.pre_build, None);
Expand All @@ -993,7 +1021,7 @@ mod tests {

let targets = &cfg_expected.targets;
let aarch64 = &targets[&Target::new_built_in("aarch64-unknown-linux-gnu")];
assert_eq!(aarch64.build_std, Some(true));
assert_eq!(aarch64.build_std, Some(BuildStd::Bool(true)));
assert_eq!(aarch64.xargo, Some(false));
assert_eq!(aarch64.image, Some(p!("test-image1")));
assert_eq!(aarch64.pre_build, None);
Expand All @@ -1002,7 +1030,7 @@ mod tests {
assert_eq!(aarch64.env.volumes, Some(vec![p!("VOL1_ARG")]));

let target2 = &targets[&Target::new_custom("target2")];
assert_eq!(target2.build_std, Some(false));
assert_eq!(target2.build_std, Some(BuildStd::Bool(false)));
assert_eq!(target2.xargo, Some(false));
assert_eq!(target2.image, Some(p!("test-image2-precedence")));
assert_eq!(target2.pre_build, None);
Expand All @@ -1011,7 +1039,7 @@ mod tests {
assert_eq!(target2.env.volumes, Some(vec![p!("VOL2_ARG_PRECEDENCE")]));

let target3 = &targets[&Target::new_custom("target3")];
assert_eq!(target3.build_std, Some(true));
assert_eq!(target3.build_std, Some(BuildStd::Bool(true)));
assert_eq!(target3.xargo, Some(false));
assert_eq!(target3.image, Some(p!("test-image3")));
assert_eq!(target3.pre_build, None);
Expand Down
36 changes: 19 additions & 17 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use cli::Args;
use color_eyre::owo_colors::OwoColorize;
use color_eyre::{Help, SectionExt};
use config::Config;
use cross_toml::BuildStd;
use rustc::{QualifiedToolchain, Toolchain};
use rustc_version::Channel;
use serde::{Deserialize, Serialize, Serializer};
Expand Down Expand Up @@ -541,7 +542,7 @@ pub fn run(
target,
uses_xargo,
uses_zig,
uses_build_std,
build_std,
zig_version,
toolchain,
is_remote,
Expand Down Expand Up @@ -586,22 +587,16 @@ pub fn run(
rustup::setup_components(
&target,
uses_xargo,
uses_build_std,
build_std.enabled(),
&toolchain,
is_nightly,
available_targets,
&args,
msg_info,
)?;

let filtered_args = get_filtered_args(
zig_version,
&args,
&target,
&config,
is_nightly,
uses_build_std,
);
let filtered_args =
get_filtered_args(zig_version, &args, &target, &config, is_nightly, &build_std);

let needs_docker = args
.subcommand
Expand Down Expand Up @@ -693,7 +688,7 @@ pub fn get_filtered_args(
target: &Target,
config: &Config,
is_nightly: bool,
uses_build_std: bool,
build_std: &BuildStd,
) -> Vec<String> {
let add_libc = |triple: &str| add_libc_version(triple, zig_version.as_deref());
let mut filtered_args = if args
Expand Down Expand Up @@ -746,9 +741,16 @@ pub fn get_filtered_args(
if is_test && config.doctests().unwrap_or_default() && is_nightly {
filtered_args.push("-Zdoctest-xcompile".to_owned());
}
if uses_build_std {
filtered_args.push("-Zbuild-std".to_owned());

if build_std.enabled() {
let mut arg = "-Zbuild-std".to_owned();
if let BuildStd::Crates(crates) = build_std {
arg.push('=');
arg.push_str(&crates.join(","));
}
filtered_args.push(arg);
}

filtered_args.extend(args.rest_args.iter().cloned());
filtered_args
}
Expand All @@ -769,8 +771,8 @@ pub fn setup(
.clone()
.or_else(|| config.target(&target_list))
.unwrap_or_else(|| Target::from(host.triple(), &target_list));
let uses_build_std = config.build_std(&target).unwrap_or(false);
let uses_xargo = !uses_build_std && config.xargo(&target).unwrap_or(!target.is_builtin());
let build_std = config.build_std(&target)?.unwrap_or_default();
let uses_xargo = !build_std.enabled() && config.xargo(&target).unwrap_or(!target.is_builtin());
let uses_zig = config.zig(&target).unwrap_or(false);
let zig_version = config.zig_version(&target)?;
let image = match docker::get_image(&config, &target, uses_zig) {
Expand Down Expand Up @@ -815,7 +817,7 @@ To override the toolchain mounted in the image, set `target.{target}.image.toolc
target,
uses_xargo,
uses_zig,
uses_build_std,
build_std,
zig_version,
toolchain,
is_remote,
Expand All @@ -830,7 +832,7 @@ pub struct CrossSetup {
pub target: Target,
pub uses_xargo: bool,
pub uses_zig: bool,
pub uses_build_std: bool,
pub build_std: BuildStd,
pub zig_version: Option<String>,
pub toolchain: QualifiedToolchain,
pub is_remote: bool,
Expand Down

0 comments on commit be13600

Please sign in to comment.