Skip to content

[WIP] support multiple --target arguments #538

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 66 additions & 5 deletions nextest-metadata/src/test_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use crate::CommandError;
use camino::{Utf8Path, Utf8PathBuf};
use serde::{Deserialize, Serialize};
use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
use std::{
borrow::Cow,
collections::{BTreeMap, BTreeSet},
Expand Down Expand Up @@ -272,9 +272,44 @@ pub struct RustBuildMetaSummary {
/// Linked paths, relative to the target directory.
pub linked_paths: BTreeSet<Utf8PathBuf>,

/// The target platform used while compiling the Rust artifacts
#[serde(default)]
pub target_platform: Option<String>,
/// The target platforms used while compiling the Rust artifacts
#[serde(
default,
rename = "target-platform",
deserialize_with = "deserialize_target_platforms"
)]
pub target_platforms: Vec<String>,
}

fn deserialize_target_platforms<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: Deserializer<'de>,
{
struct DeVisitor;

impl<'de2> Visitor<'de2> for DeVisitor {
type Value = Vec<String>;

fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "a string or a list of strings")
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(vec![v.to_owned()])
}

fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de2>,
{
Vec::<String>::deserialize(serde::de::value::SeqAccessDeserializer::new(seq))
}
}

deserializer.deserialize_any(DeVisitor)
}

/// A non-test Rust binary. Used to set the correct environment
Expand Down Expand Up @@ -484,8 +519,34 @@ mod tests {
base_output_directories: BTreeSet::new(),
non_test_binaries: BTreeMap::new(),
linked_paths: BTreeSet::new(),
target_platform: None,
target_platforms: vec![],
}; "no target platform")]
#[test_case(r#"{
"target-directory": "/foo",
"base-output-directories": [],
"non-test-binaries": {},
"linked-paths": [],
"target-platform": "x86_64-unknown-linux-gnu"
}"#, RustBuildMetaSummary {
target_directory: "/foo".into(),
base_output_directories: BTreeSet::new(),
non_test_binaries: BTreeMap::new(),
linked_paths: BTreeSet::new(),
target_platforms: vec!["x86_64-unknown-linux-gnu".to_owned()],
}; "target platform as string")]
#[test_case(r#"{
"target-directory": "/foo",
"base-output-directories": [],
"non-test-binaries": {},
"linked-paths": [],
"target-platform": ["x86_64-unknown-linux-gnu", "i686-unknown-linux-gnu"],
}"#, RustBuildMetaSummary {
target_directory: "/foo".into(),
base_output_directories: BTreeSet::new(),
non_test_binaries: BTreeMap::new(),
linked_paths: BTreeSet::new(),
target_platforms: vec!["x86_64-unknown-linux-gnu".to_owned(), "i686-unknown-linux-gnu".to_owned()],
}; "target platforms as vec")]
fn test_deserialize_old_rust_build_meta(input: &str, expected: RustBuildMetaSummary) {
let build_meta: RustBuildMetaSummary =
serde_json::from_str(input).expect("input deserialized correctly");
Expand Down
8 changes: 4 additions & 4 deletions nextest-runner/src/cargo_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ impl TargetTriple {

/// Converts a `String` that was output by `TargetTriple::serialize` back to a target triple.
/// This target triple is assumed to orginiate from a build-metadata config.
pub fn deserialize(target_triple: Option<String>) -> Option<TargetTriple> {
Some(TargetTriple {
triple: target_triple?,
pub fn deserialize(target_triple: String) -> Self {
Self {
triple: target_triple,
source: TargetTripleSource::Metadata,
})
}
}

/// Find the target triple being built.
Expand Down
63 changes: 38 additions & 25 deletions nextest-runner/src/list/binary_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ impl BinaryList {
pub fn from_messages(
reader: impl io::BufRead,
graph: &PackageGraph,
target_triple: Option<TargetTriple>,
target_platforms: Vec<TargetTriple>,
) -> Result<Self, FromMessagesError> {
let mut state = BinaryListBuildState::new(graph, target_triple);
let mut state = BinaryListBuildState::new(graph, target_platforms);

for message in Message::parse_stream(reader) {
let message = message.map_err(FromMessagesError::ReadMessages)?;
Expand Down Expand Up @@ -161,13 +161,13 @@ struct BinaryListBuildState<'g> {
}

impl<'g> BinaryListBuildState<'g> {
fn new(graph: &'g PackageGraph, target_triple: Option<TargetTriple>) -> Self {
fn new(graph: &'g PackageGraph, target_platforms: Vec<TargetTriple>) -> Self {
let rust_target_dir = graph.workspace().target_directory().to_path_buf();

Self {
graph,
rust_binaries: vec![],
rust_build_meta: RustBuildMeta::new(rust_target_dir, target_triple),
rust_build_meta: RustBuildMeta::new(rust_target_dir, target_platforms),
}
}

Expand Down Expand Up @@ -212,24 +212,28 @@ impl<'g> BinaryListBuildState<'g> {
});
}

let (computed_kind, platform) = if kind.iter().any(|k| {
// https://doc.rust-lang.org/nightly/cargo/reference/cargo-targets.html#the-crate-type-field
k == "lib" || k == "rlib" || k == "dylib" || k == "cdylib" || k == "staticlib"
}) {
(RustTestBinaryKind::LIB, BuildPlatform::Target)
} else if kind.get(0).map(String::as_str) == Some("proc-macro") {
(RustTestBinaryKind::PROC_MACRO, BuildPlatform::Host)
} else {
// Non-lib kinds should always have just one element. Grab the first one.
(
RustTestBinaryKind::new(
kind.into_iter()
.next()
.expect("already checked that kind is non-empty"),
),
BuildPlatform::Target,
)
};
let (computed_kind, platform) =
if kind.get(0).map(String::as_str) == Some("proc-macro") {
(RustTestBinaryKind::PROC_MACRO, BuildPlatform::Host)
} else {
let computed_kind = if kind.iter().any(|k| {
// https://doc.rust-lang.org/nightly/cargo/reference/cargo-targets.html#the-crate-type-field
k == "lib"
|| k == "rlib"
|| k == "dylib"
|| k == "cdylib"
|| k == "staticlib"
}) {
RustTestBinaryKind::LIB
} else {
// Non-lib kinds should always have just one element. Grab the first one.
RustTestBinaryKind::new(
kind.into_iter()
.next()
.expect("already checked that kind is non-empty"),
)
};
};

// To ensure unique binary IDs, we use the following scheme:
if computed_kind == RustTestBinaryKind::LIB {
Expand Down Expand Up @@ -307,7 +311,7 @@ impl<'g> BinaryListBuildState<'g> {
/// have a match.
///
/// The `Option` in the return value is to let ? work.
fn detect_base_output_dir(&mut self, artifact_path: &Utf8Path) -> Option<()> {
fn detect_base_output_dir(&mut self, artifact_path: &Utf8Path) -> Option<&TargetTriple> {
// Artifact paths must be relative to the target directory.
let rel_path = artifact_path
.strip_prefix(&self.rust_build_meta.target_directory)
Expand All @@ -320,8 +324,17 @@ impl<'g> BinaryListBuildState<'g> {
.base_output_directories
.insert(convert_rel_path_to_forward_slash(base));
}
// If the base has a first component, it likely indicates a target. Look over the list
// of targets to see if one of them matches this one. If it does, return that.
if let Some(target) = base.iter().next() {
return self
.rust_build_meta
.target_platforms
.iter()
.find(|t| t.triple == target);
}
}
Some(())
None
}

fn process_build_script(&mut self, build_script: BuildScript) -> Result<(), FromMessagesError> {
Expand Down Expand Up @@ -393,7 +406,7 @@ mod tests {
triple: "fake-triple".to_owned(),
source: TargetTripleSource::CliOption,
};
let mut rust_build_meta = RustBuildMeta::new("/fake/target", Some(fake_triple));
let mut rust_build_meta = RustBuildMeta::new("/fake/target", vec![fake_triple]);
rust_build_meta
.base_output_directories
.insert("my-profile".into());
Expand Down
20 changes: 12 additions & 8 deletions nextest-runner/src/list/rust_build_meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ pub struct RustBuildMeta<State> {
/// requested them. We might consider adding a new field with metadata about that.
pub linked_paths: BTreeMap<Utf8PathBuf, BTreeSet<String>>,

/// The target triple used while compiling the artifacts
pub target_triple: Option<TargetTriple>,
/// The target triples used while compiling the artifacts
pub target_platforms: Vec<TargetTriple>,

state: PhantomData<State>,
}
Expand All @@ -46,15 +46,15 @@ impl RustBuildMeta<BinaryListState> {
/// Creates a new [`RustBuildMeta`].
pub fn new(
target_directory: impl Into<Utf8PathBuf>,
target_triple: Option<TargetTriple>,
target_platforms: Vec<TargetTriple>,
) -> Self {
Self {
target_directory: target_directory.into(),
base_output_directories: BTreeSet::new(),
non_test_binaries: BTreeMap::new(),
linked_paths: BTreeMap::new(),
state: PhantomData,
target_triple,
target_platforms,
}
}

Expand All @@ -70,7 +70,7 @@ impl RustBuildMeta<BinaryListState> {
non_test_binaries: self.non_test_binaries.clone(),
linked_paths: self.linked_paths.clone(),
state: PhantomData,
target_triple: self.target_triple.clone(),
target_platforms: self.target_platforms.clone(),
}
}
}
Expand All @@ -85,7 +85,7 @@ impl RustBuildMeta<TestListState> {
non_test_binaries: BTreeMap::new(),
linked_paths: BTreeMap::new(),
state: PhantomData,
target_triple: None,
target_platforms: vec![],
}
}

Expand Down Expand Up @@ -134,7 +134,11 @@ impl<State> RustBuildMeta<State> {
.map(|linked_path| (linked_path, BTreeSet::new()))
.collect(),
state: PhantomData,
target_triple: TargetTriple::deserialize(summary.target_platform),
target_platforms: summary
.target_platforms
.into_iter()
.map(TargetTriple::deserialize)
.collect(),
}
}

Expand All @@ -145,7 +149,7 @@ impl<State> RustBuildMeta<State> {
base_output_directories: self.base_output_directories.clone(),
non_test_binaries: self.non_test_binaries.clone(),
linked_paths: self.linked_paths.keys().cloned().collect(),
target_platform: TargetTriple::serialize(self.target_triple.as_ref()),
target_platforms: TargetTriple::serialize(self.target_triple.as_ref()),
}
}
}