Skip to content

Commit 7fc6d0d

Browse files
committed
Trying to refactor config & metadata modules of cargo-gpu
1 parent f8850ce commit 7fc6d0d

File tree

8 files changed

+274
-239
lines changed

8 files changed

+274
-239
lines changed

crates/cargo-gpu/src/build.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use cargo_gpu_build::{
99
spirv_builder::{CompileResult, ModuleResult, SpirvBuilder},
1010
};
1111

12-
use crate::{install::Install, linkage::Linkage, user_consent::ask_for_user_consent};
12+
use crate::{install::InstallArgs, linkage::Linkage, user_consent::ask_for_user_consent};
1313

1414
/// Args for just a build.
1515
#[derive(Debug, Clone, clap::Parser, serde::Deserialize, serde::Serialize)]
@@ -47,12 +47,12 @@ impl Default for BuildArgs {
4747
}
4848

4949
/// `cargo build` subcommands.
50-
#[derive(Clone, Debug, clap::Parser, serde::Deserialize, serde::Serialize)]
50+
#[derive(Clone, Debug, Default, clap::Parser, serde::Deserialize, serde::Serialize)]
5151
#[non_exhaustive]
5252
pub struct Build {
5353
/// CLI args for install the `rust-gpu` compiler and components.
5454
#[clap(flatten)]
55-
pub install: Install,
55+
pub install: InstallArgs,
5656

5757
/// CLI args for configuring the build of the shader.
5858
#[clap(flatten)]
@@ -195,22 +195,25 @@ impl Build {
195195
mod test {
196196
use clap::Parser as _;
197197

198-
use crate::{Cli, Command};
198+
use crate::{
199+
test::{shader_crate_template_path, tests_teardown},
200+
Cli, Command,
201+
};
199202

200203
#[test_log::test]
201204
fn builder_from_params() {
202-
crate::test::tests_teardown();
205+
tests_teardown();
203206

204-
let shader_crate_path = crate::test::shader_crate_template_path();
207+
let shader_crate_path = shader_crate_template_path();
205208
let output_dir = shader_crate_path.join("shaders");
206209

207210
let args = [
208-
"target/debug/cargo-gpu",
209-
"build",
210-
"--shader-crate",
211-
&format!("{}", shader_crate_path.display()),
212-
"--output-dir",
213-
&format!("{}", output_dir.display()),
211+
"target/debug/cargo-gpu".as_ref(),
212+
"build".as_ref(),
213+
"--shader-crate".as_ref(),
214+
shader_crate_path.as_os_str(),
215+
"--output-dir".as_ref(),
216+
output_dir.as_os_str(),
214217
];
215218
if let Cli {
216219
command: Command::Build(build),

crates/cargo-gpu/src/config.rs

Lines changed: 91 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -1,115 +1,61 @@
11
//! Manage and merge the various sources of config: shader crate's `Cargo.toml`(s) and CLI args.
2-
use anyhow::Context as _;
3-
use clap::Parser as _;
42
5-
/// Config
6-
pub struct Config;
3+
use std::path::Path;
74

8-
impl Config {
9-
/// Get all the config defaults as JSON.
10-
pub fn defaults_as_json() -> anyhow::Result<serde_json::Value> {
11-
let defaults_json = Self::cli_args_to_json(vec![String::new()])?;
12-
Ok(defaults_json)
13-
}
14-
15-
/// Convert CLI args to their serde JSON representation.
16-
fn cli_args_to_json(env_args: Vec<String>) -> anyhow::Result<serde_json::Value> {
17-
Ok(serde_json::to_value(crate::build::Build::parse_from(
18-
env_args,
19-
))?)
20-
}
21-
22-
/// Config for the `cargo gpu build` and `cargo gpu install` can be set in the shader crate's
23-
/// `Cargo.toml`, so here we load that config first as the base config, and the CLI arguments can
24-
/// then later override it.
25-
pub fn clap_command_with_cargo_config(
26-
shader_crate_path: &std::path::PathBuf,
27-
mut env_args: Vec<String>,
28-
) -> anyhow::Result<crate::build::Build> {
29-
let mut config = crate::metadata::Metadata::as_json(shader_crate_path)?;
30-
31-
env_args.retain(|arg| !(arg == "build" || arg == "install"));
32-
let cli_args_json = Self::cli_args_to_json(env_args)?;
33-
Self::json_merge(&mut config, cli_args_json, None)?;
34-
35-
let args = serde_json::from_value::<crate::build::Build>(config)?;
36-
Ok(args)
37-
}
5+
use serde::{de::DeserializeOwned, Serialize};
6+
use serde_json::{from_value, to_value};
387

39-
/// Merge 2 JSON objects. But only if the incoming patch value isn't the default value.
40-
/// Inspired by: <https://stackoverflow.com/a/47142105/575773>
41-
pub fn json_merge(
42-
left_in: &mut serde_json::Value,
43-
right_in: serde_json::Value,
44-
maybe_pointer: Option<&String>,
45-
) -> anyhow::Result<()> {
46-
let defaults = Self::defaults_as_json()?;
47-
48-
match (left_in, right_in) {
49-
(left @ &mut serde_json::Value::Object(_), serde_json::Value::Object(right)) => {
50-
let left_as_object = left
51-
.as_object_mut()
52-
.context("Unreachable, we've already proved it's an object")?;
53-
for (key, value) in right {
54-
let new_pointer = maybe_pointer.as_ref().map_or_else(
55-
|| format!("/{}", key.clone()),
56-
|pointer| format!("{}/{}", pointer, key.clone()),
57-
);
58-
Self::json_merge(
59-
left_as_object
60-
.entry(key.clone())
61-
.or_insert(serde_json::Value::Null),
62-
value,
63-
Some(&new_pointer),
64-
)?;
65-
}
66-
}
67-
(left, right) => {
68-
if let Some(pointer) = maybe_pointer {
69-
let default = defaults.pointer(pointer).context(format!(
70-
"Configuration option with path `{pointer}` was not found in the default configuration, \
71-
which is:\ndefaults: {defaults:#?}"
72-
))?;
73-
if &right != default {
74-
// Only overwrite if the new value differs from the defaults.
75-
*left = right;
76-
}
77-
}
78-
}
79-
}
8+
use crate::{merge::json_merge, metadata::Metadata};
809

81-
Ok(())
82-
}
10+
/// Config for the subcommand can be set in the shader crate's `Cargo.toml`,
11+
/// so here we load that config first as the base config, and the CLI arguments can then later override it.
12+
pub fn clap_command_with_cargo_config<C>(shader_crate: &Path, config: &C) -> anyhow::Result<C>
13+
where
14+
C: Default + Serialize + DeserializeOwned,
15+
{
16+
let from_cargo_toml = Metadata::as_json(shader_crate)?;
17+
let from_cli = to_value(config)?;
18+
let default = to_value(C::default())?;
19+
let final_config = json_merge(from_cargo_toml, from_cli, &default);
20+
Ok(from_value(final_config)?)
8321
}
8422

8523
#[cfg(test)]
8624
mod test {
87-
use super::*;
25+
use std::{io::Write as _, path::PathBuf};
8826

89-
use std::io::Write as _;
27+
use clap::Parser as _;
28+
use spirv_builder::Capability;
29+
30+
use crate::{
31+
build::Build,
32+
test::{overwrite_shader_cargo_toml, shader_crate_test_path},
33+
};
34+
35+
use super::*;
9036

9137
#[test_log::test]
9238
fn booleans_from_cli() {
93-
let shader_crate_path = crate::test::shader_crate_test_path();
94-
95-
let args = Config::clap_command_with_cargo_config(
96-
&shader_crate_path,
97-
vec![
98-
"gpu".to_owned(),
99-
"build".to_owned(),
100-
"--debug".to_owned(),
101-
"--auto-install-rust-toolchain".to_owned(),
102-
],
103-
)
104-
.unwrap();
39+
let shader_crate_path = shader_crate_test_path();
40+
let config = Build::parse_from([
41+
"gpu".as_ref(),
42+
// "build".as_ref(),
43+
"--shader-crate".as_ref(),
44+
shader_crate_path.as_os_str(),
45+
"--debug".as_ref(),
46+
"--auto-install-rust-toolchain".as_ref(),
47+
]);
48+
49+
let args = clap_command_with_cargo_config(&shader_crate_path, &config).unwrap();
10550
assert!(!args.build.spirv_builder.release);
10651
assert!(args.install.auto_install_rust_toolchain);
10752
}
10853

10954
#[test_log::test]
11055
fn booleans_from_cargo() {
111-
let shader_crate_path = crate::test::shader_crate_test_path();
112-
let mut file = crate::test::overwrite_shader_cargo_toml(&shader_crate_path);
56+
let shader_crate_path = shader_crate_test_path();
57+
58+
let mut file = overwrite_shader_cargo_toml(&shader_crate_path);
11359
file.write_all(
11460
[
11561
"[package.metadata.rust-gpu.build]",
@@ -122,14 +68,21 @@ mod test {
12268
)
12369
.unwrap();
12470

125-
let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap();
71+
let config = Build::parse_from([
72+
"gpu".as_ref(),
73+
// "build".as_ref(),
74+
"--shader-crate".as_ref(),
75+
shader_crate_path.as_os_str(),
76+
]);
77+
78+
let args = clap_command_with_cargo_config(&shader_crate_path, &config).unwrap();
12679
assert!(!args.build.spirv_builder.release);
12780
assert!(args.install.auto_install_rust_toolchain);
12881
}
12982

130-
fn update_cargo_output_dir() -> std::path::PathBuf {
131-
let shader_crate_path = crate::test::shader_crate_test_path();
132-
let mut file = crate::test::overwrite_shader_cargo_toml(&shader_crate_path);
83+
fn update_cargo_output_dir() -> PathBuf {
84+
let shader_crate_path = shader_crate_test_path();
85+
let mut file = overwrite_shader_cargo_toml(&shader_crate_path);
13386
file.write_all(
13487
[
13588
"[package.metadata.rust-gpu.build]",
@@ -145,35 +98,41 @@ mod test {
14598
#[test_log::test]
14699
fn string_from_cargo() {
147100
let shader_crate_path = update_cargo_output_dir();
148-
149-
let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap();
101+
let config = Build::parse_from([
102+
"gpu".as_ref(),
103+
// "build".as_ref(),
104+
"--shader-crate".as_ref(),
105+
shader_crate_path.as_os_str(),
106+
]);
107+
108+
let args = clap_command_with_cargo_config(&shader_crate_path, &config).unwrap();
150109
if cfg!(target_os = "windows") {
151-
assert_eq!(args.build.output_dir, std::path::Path::new("C:/the/moon"));
110+
assert_eq!(args.build.output_dir, Path::new("C:/the/moon"));
152111
} else {
153-
assert_eq!(args.build.output_dir, std::path::Path::new("/the/moon"));
112+
assert_eq!(args.build.output_dir, Path::new("/the/moon"));
154113
}
155114
}
156115

157116
#[test_log::test]
158117
fn string_from_cargo_overwritten_by_cli() {
159118
let shader_crate_path = update_cargo_output_dir();
160-
161-
let args = Config::clap_command_with_cargo_config(
162-
&shader_crate_path,
163-
vec![
164-
"gpu".to_owned(),
165-
"build".to_owned(),
166-
"--output-dir".to_owned(),
167-
"/the/river".to_owned(),
168-
],
169-
)
170-
.unwrap();
171-
assert_eq!(args.build.output_dir, std::path::Path::new("/the/river"));
119+
let config = Build::parse_from([
120+
"gpu".as_ref(),
121+
// "build".as_ref(),
122+
"--shader-crate".as_ref(),
123+
shader_crate_path.as_os_str(),
124+
"--output-dir".as_ref(),
125+
"/the/river".as_ref(),
126+
]);
127+
128+
let args = clap_command_with_cargo_config(&shader_crate_path, &config).unwrap();
129+
assert_eq!(args.build.output_dir, Path::new("/the/river"));
172130
}
173131

174132
#[test_log::test]
175133
fn arrays_from_cargo() {
176134
let shader_crate_path = crate::test::shader_crate_test_path();
135+
177136
let mut file = crate::test::overwrite_shader_cargo_toml(&shader_crate_path);
178137
file.write_all(
179138
[
@@ -185,30 +144,33 @@ mod test {
185144
)
186145
.unwrap();
187146

188-
let args = Config::clap_command_with_cargo_config(&shader_crate_path, vec![]).unwrap();
147+
let config = Build::parse_from([
148+
"gpu".as_ref(),
149+
// "build".as_ref(),
150+
"--shader-crate".as_ref(),
151+
shader_crate_path.as_os_str(),
152+
]);
153+
154+
let args = clap_command_with_cargo_config(&shader_crate_path, &config).unwrap();
189155
assert_eq!(
190156
args.build.spirv_builder.capabilities,
191-
vec![
192-
spirv_builder::Capability::AtomicStorage,
193-
spirv_builder::Capability::Matrix
194-
]
157+
[Capability::AtomicStorage, Capability::Matrix]
195158
);
196159
}
197160

198161
#[test_log::test]
199162
fn rename_manifest_parse() {
200163
let shader_crate_path = crate::test::shader_crate_test_path();
201-
202-
let args = Config::clap_command_with_cargo_config(
203-
&shader_crate_path,
204-
vec![
205-
"gpu".to_owned(),
206-
"build".to_owned(),
207-
"--manifest-file".to_owned(),
208-
"mymanifest".to_owned(),
209-
],
210-
)
211-
.unwrap();
164+
let config = Build::parse_from([
165+
"gpu".as_ref(),
166+
// "build".as_ref(),
167+
"--shader-crate".as_ref(),
168+
shader_crate_path.as_os_str(),
169+
"--manifest-file".as_ref(),
170+
"mymanifest".as_ref(),
171+
]);
172+
173+
let args = clap_command_with_cargo_config(&shader_crate_path, &config).unwrap();
212174
assert_eq!(args.build.manifest_file, "mymanifest".to_owned());
213175
}
214176
}

0 commit comments

Comments
 (0)