Skip to content

Commit bf72856

Browse files
committed
Enhance --cargo flag handling by allowing whitespace-separated values and adding tests for proper parsing
1 parent 6d4bcd6 commit bf72856

6 files changed

Lines changed: 60 additions & 14 deletions

File tree

cargo-pgrx/src/command/install.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ pub(crate) struct Install {
5757
pub(crate) features: clap_cargo::Features,
5858
#[clap(long)]
5959
pub(crate) target: Option<String>,
60-
/// Extra flag forwarded verbatim to every `cargo` invocation (repeatable).
61-
/// e.g. `--cargo=--config=child/.cargo/config.toml --cargo=--offline`
60+
/// Extra cargo flags forwarded to every `cargo` invocation. Repeatable and split on whitespace: `--cargo=--config=foo` or `--cargo "--offline --frozen"`.
6261
#[clap(long = "cargo", value_name = "FLAG")]
6362
pub(crate) cargo: Vec<String>,
6463
#[clap(from_global, action = ArgAction::Count)]
@@ -354,7 +353,7 @@ pub(crate) fn build_extension(
354353
command.arg(arg);
355354
}
356355

357-
for arg in cargo_flags {
356+
for arg in crate::metadata::split_cargo_flags(cargo_flags) {
358357
command.arg(arg);
359358
}
360359

@@ -513,9 +512,12 @@ pub(crate) fn get_version(manifest_path: &Path, cargo_flags: &[String]) -> eyre:
513512
let version = match get_property(manifest_path, "default_version")? {
514513
Some(v) => {
515514
if v == "@CARGO_VERSION@" {
516-
let metadata =
517-
crate::metadata::metadata(&Default::default(), Some(manifest_path), cargo_flags)
518-
.wrap_err("couldn't get cargo metadata")?;
515+
let metadata = crate::metadata::metadata(
516+
&Default::default(),
517+
Some(manifest_path),
518+
cargo_flags,
519+
)
520+
.wrap_err("couldn't get cargo metadata")?;
519521
crate::metadata::validate(Some(manifest_path), &metadata)?;
520522
let manifest_path = crate::manifest::manifest_path(&metadata, None)
521523
.wrap_err("Couldn't get manifest path")?;

cargo-pgrx/src/command/package.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,7 @@ pub(crate) struct Package {
4646
pub(crate) features: clap_cargo::Features,
4747
#[clap(long)]
4848
pub(crate) target: Option<String>,
49-
/// Extra flag forwarded verbatim to every `cargo` invocation (repeatable).
50-
/// e.g. `--cargo=--config=child/.cargo/config.toml --cargo=--offline`
49+
/// Extra cargo flags forwarded to every `cargo` invocation. Repeatable and on whitespace: `--cargo=--config=foo` or `--cargo "--offline --frozen"`.
5150
#[clap(long = "cargo", value_name = "FLAG")]
5251
pub(crate) cargo: Vec<String>,
5352
#[clap(from_global, action = ArgAction::Count)]

cargo-pgrx/src/command/run.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ pub(crate) struct Run {
5656
install_only: bool,
5757
#[clap(long)]
5858
valgrind: bool,
59-
/// Extra flag forwarded verbatim to every `cargo` invocation (repeatable).
60-
/// e.g. `--cargo=--config=child/.cargo/config.toml --cargo=--offline`
59+
/// Extra cargo flags forwarded to every `cargo` invocation. Repeatable and split on whitespace: `--cargo=--config=foo` or `--cargo "--offline --frozen"`.
6160
#[clap(long = "cargo", value_name = "FLAG")]
6261
cargo: Vec<String>,
6362
}

cargo-pgrx/src/command/test.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ pub(crate) struct Test {
4949
pgdata: Option<PathBuf>,
5050
#[clap(flatten)]
5151
features: clap_cargo::Features,
52-
/// Extra flag forwarded verbatim to every `cargo` invocation (repeatable).
53-
/// e.g. `--cargo=--config=child/.cargo/config.toml --cargo=--offline`
52+
/// Extra cargo flags forwarded to every `cargo` invocation. Repeatable and split on whitespace: `--cargo=--config=foo` or `--cargo "--offline --frozen"`.
5453
#[clap(long = "cargo", value_name = "FLAG")]
5554
cargo: Vec<String>,
5655
#[clap(from_global, action = clap::ArgAction::Count)]
@@ -176,7 +175,7 @@ pub fn test_extension(
176175
.env("PGRX_NO_SCHEMA", if no_schema { "true" } else { "false" });
177176

178177
// The `--cargo` passthrough reaches every cargo invocation; here, `cargo test`.
179-
for arg in cargo_flags {
178+
for arg in crate::metadata::split_cargo_flags(cargo_flags) {
180179
command.arg(arg);
181180
}
182181

cargo-pgrx/src/metadata.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,24 @@ pub fn metadata(
2626

2727
// The `--cargo` passthrough must reach `cargo metadata` — the first cargo
2828
// invocation, where config issues (private registries, etc.) fail first.
29+
let cargo_flags = split_cargo_flags(cargo_flags);
2930
if !cargo_flags.is_empty() {
30-
metadata_command.other_options(cargo_flags.to_vec());
31+
metadata_command.other_options(cargo_flags);
3132
}
3233
features.forward_metadata(&mut metadata_command);
3334
let metadata = metadata_command.exec()?;
3435
Ok(metadata)
3536
}
3637

38+
/// Split each `--cargo` value on whitespace, so both the single-token form
39+
/// (`--cargo=--config=child/.cargo/config.toml`) and the quoted-string form
40+
/// (`--cargo "--config foo"`) work, forwarding each token as one argv element.
41+
// ponytail: naive whitespace split, same as PGRX_BUILD_FLAGS in cargo.rs; a
42+
// config path containing spaces needs the single-token `--cargo=--config=x` form.
43+
pub fn split_cargo_flags(cargo_flags: &[String]) -> Vec<String> {
44+
cargo_flags.iter().flat_map(|f| f.split_ascii_whitespace().map(str::to_owned)).collect()
45+
}
46+
3747
#[tracing::instrument(level = "error", skip_all)]
3848
pub fn validate(path: Option<&Path>, metadata: &Metadata) -> eyre::Result<()> {
3949
let cargo_pgrx_version = env!("CARGO_PKG_VERSION");
@@ -98,3 +108,27 @@ cargo-pgrx and pgrx library versions must be identical.
98108

99109
Ok(())
100110
}
111+
112+
#[cfg(test)]
113+
mod tests {
114+
use super::split_cargo_flags;
115+
116+
#[test]
117+
fn split_cargo_flags_handles_both_forms() {
118+
// single-token form is passed through untouched
119+
assert_eq!(
120+
split_cargo_flags(&["--config=child/.cargo/config.toml".into()]),
121+
vec!["--config=child/.cargo/config.toml"]
122+
);
123+
// quoted-string form is split into separate argv tokens
124+
assert_eq!(
125+
split_cargo_flags(&["--config foo --offline".into()]),
126+
vec!["--config", "foo", "--offline"]
127+
);
128+
// repeatable `--cargo` and whitespace-splitting compose
129+
assert_eq!(
130+
split_cargo_flags(&["--offline".into(), "--config foo".into()]),
131+
vec!["--offline", "--config", "foo"]
132+
);
133+
}
134+
}

cargo-pgrx/tests/cargo_flags_passthrough.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ fn cargo_flag_reaches_cargo_metadata() {
6565
);
6666
}
6767

68+
/// The quoted-string form (`--cargo "--flag-a --flag-b"`) is split on
69+
/// whitespace: two valid cargo flags packed into one `--cargo` value get past
70+
/// `cargo metadata`. Without splitting, the mashed-together token would be an
71+
/// unknown argument and `cargo metadata` would reject it.
72+
#[test]
73+
fn cargo_flag_whitespace_string_is_split() {
74+
let out = install_with("split", &["--cargo", "--offline --color=never"], &[]);
75+
assert!(
76+
!out.contains("couldn't get cargo metadata"),
77+
"two valid flags in one --cargo value should be split and pass `cargo metadata`, got:\n{out}"
78+
);
79+
}
80+
6881
/// `PGRX_BUILD_FLAGS` must stay build-only: a bogus flag there must NOT break
6982
/// `cargo metadata` (the run gets past it and fails later, for other reasons).
7083
/// This is the property that keeps `--cargo` distinct from build-only flags.

0 commit comments

Comments
 (0)