Skip to content

Commit ae55312

Browse files
multivers: add the --cpus option similar to the one in Cargo.toml
1 parent 38b0b32 commit ae55312

File tree

5 files changed

+65
-32
lines changed

5 files changed

+65
-32
lines changed

src/cli.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ pub enum Cargo {
77
Multivers(Args),
88
}
99

10+
/// Type of information to print on stdout
1011
#[derive(clap::ValueEnum, Clone, Copy)]
1112
pub enum Print {
13+
/// Prints the list of CPU features supported by the target
1214
CpuFeatures,
1315
}
1416

@@ -22,6 +24,15 @@ pub struct Args {
2224
#[clap(long, value_name = "INFORMATION")]
2325
pub print: Option<Print>,
2426

27+
/// Comma-separated list of CPUs to use as a target
28+
#[clap(
29+
long,
30+
use_value_delimiter = true,
31+
value_delimiter = ',',
32+
value_name = "CPUs"
33+
)]
34+
pub cpus: Option<Vec<String>>,
35+
2536
/// Comma-separated list of CPU features to exclude from the builds
2637
#[clap(
2738
long,
@@ -46,6 +57,7 @@ pub struct Args {
4657
}
4758

4859
impl Args {
60+
/// Returns the target given on the command line or the default target that rustc uses to build if none is provided
4961
pub fn target(&self) -> anyhow::Result<String> {
5062
self.target
5163
.as_ref()

src/features.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ pub struct Cpus {
4040
}
4141

4242
impl Cpus {
43-
pub fn builder<'a>(target: impl Into<String>) -> anyhow::Result<CpusBuilder<'a>> {
44-
CpusBuilder::new(target)
43+
pub fn builder<'a>(
44+
target: impl Into<String>,
45+
cpus: Option<Vec<String>>,
46+
) -> anyhow::Result<CpusBuilder<'a>> {
47+
CpusBuilder::new(target, cpus)
4548
}
4649

4750
/// Returns a sorted and deduplicated iterator of CPU features set for each CPU
@@ -72,12 +75,16 @@ pub struct CpusBuilder<'a> {
7275
}
7376

7477
impl<'a> CpusBuilder<'a> {
75-
pub fn new(target: impl Into<String>) -> anyhow::Result<Self> {
78+
pub fn new(target: impl Into<String>, cpus: Option<Vec<String>>) -> anyhow::Result<Self> {
7679
let target = target.into();
7780
let triple = Triple::from_str(&target).context("Failed to parse the target")?;
78-
let iter = Rustc::cpus_from_target(&target)
79-
.context("Failed to get the set of CPUs for the target")?
80-
.into_par_iter();
81+
let iter = if let Some(cpus) = cpus {
82+
cpus
83+
} else {
84+
Rustc::cpus_from_target(&target)
85+
.context("Failed to get the set of CPUs for the target")?
86+
}
87+
.into_par_iter();
8188

8289
Ok(Self {
8390
iter,

src/main.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn main() -> anyhow::Result<()> {
2222
if matches!(args.print, Some(Print::CpuFeatures)) {
2323
let target = args.target()?;
2424

25-
let cpus = Cpus::builder(&target)
25+
let cpus = Cpus::builder(&target, args.cpus)
2626
.context("Failed to get the set of CPU features for the target")?
2727
.build();
2828
let mut stdout = std::io::stdout().lock();
@@ -38,8 +38,5 @@ fn main() -> anyhow::Result<()> {
3838
"You must run cargo multivers with Rust nightly channel. For example, you can run: `cargo +nightly multivers`"
3939
);
4040

41-
let multivers = Multivers::from_args(args)?;
42-
multivers.build()?;
43-
44-
Ok(())
41+
Multivers::from_args(args)?.build()
4542
}

src/metadata.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,15 @@ impl<'de> Deserialize<'de> for ArchitectureWrapper {
3030

3131
#[derive(PartialEq, Eq, Hash, Debug)]
3232
pub struct TargetMetadata {
33-
pub cpus: Vec<String>,
33+
pub cpus: Option<Vec<String>>,
3434
}
3535

3636
impl<'de> Deserialize<'de> for TargetMetadata {
3737
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3838
where
3939
D: Deserializer<'de>,
4040
{
41-
let mut metadata = HashMap::<String, Vec<String>>::deserialize(deserializer)?;
41+
let mut metadata = HashMap::<String, Option<Vec<String>>>::deserialize(deserializer)?;
4242
let cpus = metadata.remove("cpus").unwrap_or_default();
4343

4444
Ok(Self { cpus })
@@ -55,7 +55,7 @@ impl<'de> Deserialize<'de> for TargetMetadata {
5555
/// ```
5656
#[derive(Debug)]
5757
pub struct MultiversMetadata {
58-
pub targets: HashMap<Architecture, TargetMetadata>,
58+
targets: HashMap<Architecture, TargetMetadata>,
5959
}
6060

6161
impl MultiversMetadata {
@@ -67,12 +67,14 @@ impl MultiversMetadata {
6767

6868
let mut metadata: HashMap<String, serde_json::Value> =
6969
serde_json::from_value(package.metadata.clone())?;
70-
let metadata = match metadata.remove("multivers") {
71-
Some(metadata) => metadata,
72-
None => return Ok(None),
70+
let Some(metadata) = metadata.remove("multivers") else {
71+
return Ok(None);
7372
};
7473

7574
let targets: HashMap<ArchitectureWrapper, _> = serde_json::from_value(metadata)?;
75+
if targets.is_empty() {
76+
return Ok(None);
77+
}
7678

7779
Ok(Some(Self {
7880
targets: targets
@@ -81,4 +83,8 @@ impl MultiversMetadata {
8183
.collect(),
8284
}))
8385
}
86+
87+
pub fn get(&self, k: &Architecture) -> Option<&TargetMetadata> {
88+
self.targets.get(k)
89+
}
8490
}

src/multivers.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ struct BuildsDescription {
4343
builds: Vec<BuildDescription>,
4444
}
4545

46+
/// Build multiple versions of the same binary, each with a different CPU features set, merged into a single portable optimized binary
4647
pub struct Multivers {
4748
metadata: Metadata,
4849
target: String,
@@ -68,7 +69,7 @@ impl Multivers {
6869
// See https://github.com/rust-lang/cargo/issues/4423
6970
let target = args.target()?;
7071

71-
let cpus = Cpus::builder(&target)
72+
let cpus = Cpus::builder(&target, args.cpus)
7273
.context("Failed to get the set of CPU features for the target")?
7374
.exclude_features(args.exclude_cpu_features.as_deref())
7475
.build();
@@ -101,19 +102,17 @@ impl Multivers {
101102
})
102103
}
103104

104-
fn build_package(&self, package: &Package) -> anyhow::Result<BuildsDescription> {
105-
let triple = Triple::from_str(&self.target).context("Failed to parse the target")?;
106-
let manifest_path = package.manifest_path.as_std_path().to_path_buf();
107-
let features_list = self.features.features.join(" ");
108-
let mut rust_flags = std::env::var("RUST_FLAGS").unwrap_or_default();
109-
110-
let metadata = MultiversMetadata::from_package(package)
111-
.context("Failed to parse package's metadata")?;
112-
let cpu_features: Vec<CpuFeatures> = if let Some(metadata) = metadata {
113-
if let Some(target_metadata) = metadata.targets.get(&triple.architecture) {
114-
target_metadata
115-
.cpus
116-
.iter()
105+
fn cpu_features(
106+
&self,
107+
triple: &Triple,
108+
metadata: Option<&MultiversMetadata>,
109+
) -> Vec<CpuFeatures> {
110+
if let Some(metadata) = metadata {
111+
if let Some(cpus) = metadata
112+
.get(&triple.architecture)
113+
.and_then(|t| t.cpus.as_ref())
114+
{
115+
cpus.iter()
117116
.filter_map(|cpu| self.cpus.get(cpu))
118117
.unique()
119118
.cloned()
@@ -123,7 +122,19 @@ impl Multivers {
123122
}
124123
} else {
125124
self.cpus.features_sets().cloned().collect()
126-
};
125+
}
126+
}
127+
128+
fn build_package(&self, package: &Package) -> anyhow::Result<BuildsDescription> {
129+
let triple: Triple =
130+
Triple::from_str(&self.target).context("Failed to parse the target")?;
131+
let manifest_path = package.manifest_path.as_std_path().to_path_buf();
132+
let features_list = self.features.features.join(" ");
133+
let mut rust_flags = std::env::var("RUST_FLAGS").unwrap_or_default();
134+
135+
let metadata = MultiversMetadata::from_package(package)
136+
.context("Failed to parse package's metadata")?;
137+
let cpu_features = self.cpu_features(&triple, metadata.as_ref());
127138

128139
self.progress.set_length(cpu_features.len() as u64);
129140
self.progress.set_prefix("Building");

0 commit comments

Comments
 (0)