Skip to content

Commit dc4dfa5

Browse files
committed
output configured targets
1 parent bb3df1b commit dc4dfa5

File tree

7 files changed

+166
-4
lines changed

7 files changed

+166
-4
lines changed

src/bin/commands/list_targets.rs

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
use std::collections::{BTreeMap, BTreeSet};
2+
use std::str::FromStr;
3+
4+
use clap::builder::PossibleValue;
5+
use clap::{Args, Subcommand};
6+
use cross::docker::{self, CROSS_CUSTOM_DOCKERFILE_IMAGE_PREFIX};
7+
use cross::shell::MessageInfo;
8+
use cross::{CommandExt, TargetList};
9+
10+
// known image prefixes, with their registry
11+
// the docker.io registry can also be implicit
12+
const GHCR_IO: &str = docker::CROSS_IMAGE;
13+
const RUST_EMBEDDED: &str = "rustembedded/cross";
14+
const DOCKER_IO: &str = "docker.io/rustembedded/cross";
15+
const IMAGE_PREFIXES: &[&str] = &[GHCR_IO, DOCKER_IO, RUST_EMBEDDED];
16+
17+
#[derive(Args, Debug)]
18+
pub struct ListTargets {
19+
/// Format version
20+
#[clap(long)]
21+
format_version: Option<FormatVersion>,
22+
/// Coloring: auto, always, never
23+
#[clap(long)]
24+
pub color: Option<String>,
25+
}
26+
27+
#[derive(Debug, Clone, Copy, serde::Serialize)]
28+
pub enum FormatVersion {
29+
#[serde(rename = "1")]
30+
One,
31+
}
32+
33+
#[derive(Debug, thiserror::Error)]
34+
#[error("invalid format version")]
35+
pub struct FormatVersionError;
36+
37+
impl FromStr for FormatVersion {
38+
type Err = FormatVersionError;
39+
40+
fn from_str(s: &str) -> Result<Self, Self::Err> {
41+
match s {
42+
"1" => Ok(FormatVersion::One),
43+
_ => Err(FormatVersionError),
44+
}
45+
}
46+
}
47+
48+
#[derive(serde::Serialize)]
49+
pub struct Output {
50+
format_version: FormatVersion,
51+
#[serde(flatten)]
52+
other: serde_json::Value,
53+
}
54+
55+
impl ListTargets {
56+
pub fn verbose(&self) -> bool {
57+
false
58+
}
59+
60+
pub fn quiet(&self) -> bool {
61+
false
62+
}
63+
64+
pub fn color(&self) -> Option<&str> {
65+
self.color.as_deref()
66+
}
67+
68+
pub fn run(self, msg_info: &mut MessageInfo) -> cross::Result<()> {
69+
let toml = if let Some(metadata) = cross::cargo_metadata_with_args(None, None, msg_info)? {
70+
cross::toml(&metadata, msg_info)?
71+
} else {
72+
None
73+
};
74+
75+
let config = cross::config::Config::new(toml);
76+
let version = if let Some(version) = self.format_version {
77+
version
78+
} else {
79+
msg_info.warn(
80+
"please specify `--format-version` flag explicitly to avoid compatibility problems",
81+
)?;
82+
FormatVersion::One
83+
};
84+
let data = match version {
85+
FormatVersion::One => self.run_v1(&config, msg_info)?,
86+
};
87+
println!(
88+
"{}",
89+
serde_json::to_string_pretty(&Output {
90+
format_version: version,
91+
other: data,
92+
})?
93+
);
94+
Ok(())
95+
}
96+
97+
pub fn run_v1(
98+
self,
99+
config: &cross::config::Config,
100+
_msg_info: &mut MessageInfo,
101+
) -> cross::Result<serde_json::Value> {
102+
#[derive(serde::Serialize)]
103+
struct Target {
104+
triplet: String,
105+
platforms: Vec<String>,
106+
}
107+
let mut targets: Vec<_> = cross::docker::PROVIDED_IMAGES
108+
.iter()
109+
.filter_map(|i| {
110+
Some(Target {
111+
triplet: Some(i.name).filter(|i| *i != "zig")?.to_owned(),
112+
platforms: i.platforms.iter().map(ToString::to_string).collect(),
113+
})
114+
})
115+
.collect();
116+
if let Some(toml_targets) = config.targets() {
117+
for (target, config) in toml_targets {
118+
if targets.iter().any(|t| t.triplet == target.triple()) {
119+
continue;
120+
}
121+
targets.push(Target {
122+
triplet: target.triple().to_owned(),
123+
platforms: config
124+
.image
125+
.as_ref()
126+
.map(|i| {
127+
i.toolchain
128+
.iter()
129+
.map(ToString::to_string)
130+
.collect::<Vec<_>>()
131+
})
132+
.filter(|v| !v.is_empty())
133+
.unwrap_or_else(|| vec![docker::ImagePlatform::DEFAULT.to_string()]),
134+
})
135+
}
136+
}
137+
Ok(serde_json::json!({
138+
"targets": targets,
139+
}))
140+
}
141+
}

src/bin/commands/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
mod clean;
22
mod containers;
33
mod images;
4+
mod list_targets;
45

56
pub use self::clean::*;
67
pub use self::containers::*;
78
pub use self::images::*;
9+
pub use self::list_targets::*;

src/bin/cross-util.rs

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ enum Commands {
3939
Containers(commands::Containers),
4040
/// Clean all cross data in local storage.
4141
Clean(commands::Clean),
42+
/// List all cross targets, including those specialized for the current project (if in one).
43+
ListTargets(commands::ListTargets),
4244
}
4345

4446
fn is_toolchain(toolchain: &str) -> cross::Result<Toolchain> {
@@ -103,6 +105,10 @@ pub fn main() -> cross::Result<()> {
103105
let engine = get_engine!(args, false, msg_info)?;
104106
args.run(engine, &mut msg_info)?;
105107
}
108+
Commands::ListTargets(args) => {
109+
let mut msg_info = get_msg_info!(args)?;
110+
args.run(&mut msg_info)?;
111+
}
106112
}
107113

108114
Ok(())

src/config.rs

+7
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,13 @@ impl Config {
212212
}
213213
}
214214

215+
/// Grabs all defined targets in toml
216+
pub fn targets(
217+
&self,
218+
) -> Option<impl Iterator<Item = (&Target, &crate::cross_toml::CrossTargetConfig)>> {
219+
self.toml.as_ref().map(|toml| toml.targets.iter())
220+
}
221+
215222
pub fn confusable_target(&self, target: &Target, msg_info: &mut MessageInfo) -> Result<()> {
216223
if let Some(keys) = self.toml.as_ref().map(|t| t.targets.keys()) {
217224
for mentioned_target in keys {

src/cross_toml.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub struct CrossTargetConfig {
4343
#[serde(default, deserialize_with = "opt_string_bool_or_struct")]
4444
zig: Option<CrossZigConfig>,
4545
#[serde(default, deserialize_with = "opt_string_or_struct")]
46-
image: Option<PossibleImage>,
46+
pub image: Option<PossibleImage>,
4747
#[serde(default, deserialize_with = "opt_string_or_struct")]
4848
dockerfile: Option<CrossTargetDockerfileConfig>,
4949
#[serde(default, deserialize_with = "opt_string_or_string_vec")]

src/docker/image.rs

+6
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,12 @@ impl Serialize for ImagePlatform {
155155
}
156156
}
157157

158+
impl std::fmt::Display for ImagePlatform {
159+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160+
self.serialize(f)
161+
}
162+
}
163+
158164
impl std::str::FromStr for ImagePlatform {
159165
type Err = eyre::Report;
160166
// [os/arch[/variant]=]toolchain

src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -279,13 +279,13 @@ impl Target {
279279
triple: TargetTriple::DEFAULT,
280280
};
281281

282-
fn new_built_in(triple: &str) -> Self {
282+
pub fn new_built_in(triple: &str) -> Self {
283283
Target::BuiltIn {
284284
triple: triple.into(),
285285
}
286286
}
287287

288-
fn new_custom(triple: &str) -> Self {
288+
pub fn new_custom(triple: &str) -> Self {
289289
Target::Custom {
290290
triple: triple.into(),
291291
}
@@ -861,7 +861,7 @@ macro_rules! commit_info {
861861
///
862862
/// The values from `CROSS_CONFIG` or `Cross.toml` are concatenated with the package
863863
/// metadata in `Cargo.toml`, with `Cross.toml` having the highest priority.
864-
fn toml(metadata: &CargoMetadata, msg_info: &mut MessageInfo) -> Result<Option<CrossToml>> {
864+
pub fn toml(metadata: &CargoMetadata, msg_info: &mut MessageInfo) -> Result<Option<CrossToml>> {
865865
let root = &metadata.workspace_root;
866866
let cross_config_path = match env::var("CROSS_CONFIG") {
867867
Ok(var) => PathBuf::from(var),

0 commit comments

Comments
 (0)