Skip to content
Open
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
33 changes: 32 additions & 1 deletion cargo-pgrx/src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub struct Cargo {
more_args: BTreeMap<String, Vec<String>>,
// Extra arguments passed after `cargo rustc --`.
rustc_args: Vec<String>,
// `--cargo` passthrough flags, forwarded verbatim (after whitespace-splitting).
cargo_flags: Vec<String>,
}

impl Cargo {
Expand Down Expand Up @@ -75,6 +77,12 @@ impl Cargo {
self
}

/// `--cargo` passthrough flags, forwarded to this cargo invocation.
pub fn cargo_flags(mut self, flags: &[String]) -> Self {
self.cargo_flags = crate::metadata::split_cargo_flags(flags);
self
}

pub fn rustc_args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
Expand Down Expand Up @@ -110,6 +118,7 @@ impl Cargo {
subcmd: _,
more_args,
rustc_args,
cargo_flags,
} = self;

// set most-interesting flags first, like profile, target, and manifest-path
Expand Down Expand Up @@ -152,12 +161,17 @@ impl Cargo {
cmd.arg(features.features.join(" "));
}

// And now the miscellaneous build flags!
// And now the miscellaneous build flags!, `PGRX_BUILD_FLAGS` stays build-only.
let flags = env::var("PGRX_BUILD_FLAGS").unwrap_or_default();
for arg in flags.split_ascii_whitespace() {
cmd.arg(arg);
}

// `--cargo` passthrough: forward each (already whitespace-split) token verbatim.
for arg in cargo_flags {
cmd.arg(arg);
}

// set envs
if let Some(log_level) = log_level {
cmd.env("RUST_LOG", log_level);
Expand Down Expand Up @@ -393,4 +407,21 @@ mod tests {
"expected no-GC rustc args after `--`: {args:?}"
);
}

#[test]
fn cargo_flags_are_forwarded_and_whitespace_split() {
// `cargo pgrx schema` (and other builds) route `--cargo` through the
// `Cargo` builder; both the single-token and quoted-string forms must
// reach the spawned command as separate argv tokens.
let command = Cargo::default()
.subcommand("rustc")
.cargo_flags(&["--offline".into(), "--config foo".into()])
.into_command();
let args =
command.get_args().map(|arg| arg.to_string_lossy().into_owned()).collect::<Vec<_>>();

assert!(args.contains(&"--offline".to_string()), "missing --offline: {args:?}");
assert!(args.contains(&"--config".to_string()), "quoted form not split: {args:?}");
assert!(args.contains(&"foo".to_string()), "quoted form not split: {args:?}");
}
}
31 changes: 30 additions & 1 deletion cargo-pgrx/src/command/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ pub(crate) struct Bench {
features: clap_cargo::Features,
#[clap(long)]
target: Option<String>,
/// Extra cargo flags forwarded to every `cargo` invocation. Repeatable and split on whitespace: `--cargo=--config=foo` or `--cargo "--offline --frozen"`.
#[clap(long = "cargo", value_name = "FLAG", allow_hyphen_values = true)]
cargo: Vec<String>,
#[clap(from_global, action = clap::ArgAction::Count)]
verbose: u8,
/// Custom `postgresql.conf` settings in the form of `key=value`
Expand All @@ -99,6 +102,7 @@ impl CommandExecute for Bench {
&self.features,
self.package.as_deref(),
self.manifest_path.as_deref(),
&self.cargo,
)?;
let mut resolved_features = self.features.clone();
let (pg_config, _) = pg_config_and_version(
Expand All @@ -117,7 +121,7 @@ impl CommandExecute for Bench {
}

let postgresql_conf = collect_postgresql_conf_settings(&self.postgresql_conf)?;
let extversion = crate::command::install::get_version(&package_manifest_path)?;
let extversion = crate::command::install::get_version(&package_manifest_path, &self.cargo)?;
let mut features = resolved_features;
ensure_feature(&mut features, "pg_bench");
let profile = CargoProfile::from_flags(
Expand All @@ -138,6 +142,7 @@ impl CommandExecute for Bench {
false,
self.target.as_deref(),
&postgresql_conf,
&self.cargo,
)?;

if self.resetdb {
Expand Down Expand Up @@ -2302,4 +2307,28 @@ mod tests {
assert_eq!(format_wait_duration(1), "1 second");
assert_eq!(format_wait_duration(2), "2 seconds");
}

#[test]
fn cargo_flag_defaults_to_empty() {
let bench = parse_bench(&["cargo", "pgrx", "bench"]);
assert!(bench.cargo.is_empty());
}

#[test]
fn cargo_flag_is_repeatable() {
let bench = parse_bench(&[
"cargo",
"pgrx",
"bench",
"--cargo=--offline",
"--cargo",
"--config foo",
]);
assert_eq!(bench.cargo, vec!["--offline".to_string(), "--config foo".to_string()]);
// and the shared splitter tokenizes each value the same way build/metadata will
assert_eq!(
crate::metadata::split_cargo_flags(&bench.cargo),
vec!["--offline", "--config", "foo"]
);
}
}
1 change: 1 addition & 0 deletions cargo-pgrx/src/command/connect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impl CommandExecute for Connect {
&Features::default(),
self.package.as_deref(),
self.manifest_path.as_deref(),
&[],
)?;
let (pg_config, _pg_version) = match pg_config_and_version(
&pgrx,
Expand Down
2 changes: 1 addition & 1 deletion cargo-pgrx/src/command/get.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl CommandExecute for Get {
#[tracing::instrument(level = "error", skip(self))]
fn execute(self) -> eyre::Result<()> {
let metadata =
crate::metadata::metadata(&Default::default(), self.manifest_path.as_deref())
crate::metadata::metadata(&Default::default(), self.manifest_path.as_deref(), &[])
.wrap_err("couldn't get cargo metadata")?;
crate::metadata::validate(self.manifest_path.as_deref(), &metadata)?;
let package_manifest_path =
Expand Down
49 changes: 38 additions & 11 deletions cargo-pgrx/src/command/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ pub(crate) struct Install {
pub(crate) features: clap_cargo::Features,
#[clap(long)]
pub(crate) target: Option<String>,
/// Extra cargo flags forwarded to every `cargo` invocation. Repeatable and split on whitespace: `--cargo=--config=foo` or `--cargo "--offline --frozen"`.
#[clap(long = "cargo", value_name = "FLAG", allow_hyphen_values = true)]
pub(crate) cargo: Vec<String>,
#[clap(from_global, action = ArgAction::Count)]
pub(crate) verbose: u8,
}
Expand All @@ -72,8 +75,10 @@ impl CommandExecute for Install {
return sudo_install.execute();
}

let metadata = crate::metadata::metadata(&self.features, self.manifest_path.as_deref())
.wrap_err("couldn't get cargo metadata")?;
let cargo_flags = std::mem::take(&mut self.cargo);
let metadata =
crate::metadata::metadata(&self.features, self.manifest_path.as_deref(), &cargo_flags)
.wrap_err("couldn't get cargo metadata")?;
crate::metadata::validate(self.manifest_path.as_deref(), &metadata)?;
let package_manifest_path =
crate::manifest::manifest_path(&metadata, self.package.as_deref())
Expand Down Expand Up @@ -110,6 +115,7 @@ impl CommandExecute for Install {
None,
&self.features,
self.target.as_deref(),
&cargo_flags,
)?;
Ok(())
}
Expand Down Expand Up @@ -147,6 +153,7 @@ pub(crate) fn install_extension(
base_directory: Option<PathBuf>,
features: &clap_cargo::Features,
target: Option<&str>,
cargo_flags: &[String],
) -> eyre::Result<Vec<PathBuf>> {
let mut output_tracking = Vec::new();

Expand All @@ -158,7 +165,7 @@ pub(crate) fn install_extension(
let build_manifest_path =
manifest_path_for_build(user_manifest_path, user_package, package_manifest_path);
let build_command_output =
build_extension(build_manifest_path, user_package, profile, features, target)?;
build_extension(build_manifest_path, user_package, profile, features, target, cargo_flags)?;
let build_command_bytes = build_command_output.stdout;
let build_command_reader = BufReader::new(build_command_bytes.as_slice());
let build_command_stream = CargoMessage::parse_stream(build_command_reader);
Expand Down Expand Up @@ -192,12 +199,13 @@ pub(crate) fn install_extension(
true,
package_manifest_path,
&mut output_tracking,
cargo_flags,
)?;
}

{
let so_name = if versioned_so {
let extver = get_version(package_manifest_path)?;
let extver = get_version(package_manifest_path, cargo_flags)?;
// note: versioned so-name format must agree with pgrx-utils
format!("{extname}-{extver}")
} else {
Expand Down Expand Up @@ -234,6 +242,7 @@ pub(crate) fn install_extension(
false,
package_manifest_path,
&mut output_tracking,
cargo_flags,
)?;
}

Expand All @@ -248,6 +257,7 @@ pub(crate) fn install_extension(
&extdir,
true,
&mut output_tracking,
cargo_flags,
)?;

println!("{} installing {}", " Finished".bold().green(), extname);
Expand All @@ -261,6 +271,7 @@ fn copy_file(
do_filter: bool,
package_manifest_path: &Path,
output_tracking: &mut Vec<PathBuf>,
cargo_flags: &[String],
) -> eyre::Result<()> {
let Some(dest_dir) = dest.parent() else {
// what fresh hell could ever cause such an error?
Expand All @@ -282,7 +293,7 @@ fn copy_file(
// we want to filter the contents of the file we're to copy
let input = fs::read_to_string(src)
.wrap_err_with(|| format!("failed to read `{}`", src.display()))?;
let input = filter_contents(package_manifest_path, input)?;
let input = filter_contents(package_manifest_path, input, cargo_flags)?;

fs::write(&dest, input).wrap_err_with(|| {
format!("failed writing `{}` to `{}`", src.display(), dest.display())
Expand All @@ -303,6 +314,7 @@ pub(crate) fn build_extension(
profile: &CargoProfile,
features: &clap_cargo::Features,
target: Option<&str>,
cargo_flags: &[String],
) -> eyre::Result<std::process::Output> {
let flags = std::env::var("PGRX_BUILD_FLAGS").unwrap_or_default();

Expand Down Expand Up @@ -341,6 +353,10 @@ pub(crate) fn build_extension(
command.arg(arg);
}

for arg in crate::metadata::split_cargo_flags(cargo_flags) {
command.arg(arg);
}

if let Some(target) = target {
command.arg("--target");
command.arg(target);
Expand Down Expand Up @@ -388,10 +404,11 @@ fn copy_sql_files(
extdir: &Path,
skip_build: bool,
output_tracking: &mut Vec<PathBuf>,
cargo_flags: &[String],
) -> eyre::Result<()> {
let (_, extname) = find_control_file(package_manifest_path)?;
{
let version = get_version(package_manifest_path)?;
let version = get_version(package_manifest_path, cargo_flags)?;
let filename = format!("{extname}--{version}.sql");
let dest = extdir.join(filename);

Expand All @@ -412,6 +429,7 @@ fn copy_sql_files(
// explicit ALTER EXTENSION would be redundant.
false,
output_tracking,
cargo_flags,
)?;
}

Expand All @@ -432,6 +450,7 @@ fn copy_sql_files(
true,
package_manifest_path,
output_tracking,
cargo_flags,
)?;
}
}
Expand Down Expand Up @@ -482,7 +501,7 @@ pub(crate) fn find_library_file(

static CARGO_VERSION: OnceLock<MemoizeKeyValue> = OnceLock::new();

pub(crate) fn get_version(manifest_path: &Path) -> eyre::Result<String> {
pub(crate) fn get_version(manifest_path: &Path, cargo_flags: &[String]) -> eyre::Result<String> {
let path_string = manifest_path.to_owned();

if let Some(version) =
Expand All @@ -494,8 +513,12 @@ pub(crate) fn get_version(manifest_path: &Path) -> eyre::Result<String> {
let version = match get_property(manifest_path, "default_version")? {
Some(v) => {
if v == "@CARGO_VERSION@" {
let metadata = crate::metadata::metadata(&Default::default(), Some(manifest_path))
.wrap_err("couldn't get cargo metadata")?;
let metadata = crate::metadata::metadata(
&Default::default(),
Some(manifest_path),
cargo_flags,
)
.wrap_err("couldn't get cargo metadata")?;
crate::metadata::validate(Some(manifest_path), &metadata)?;
let manifest_path = crate::manifest::manifest_path(&metadata, None)
.wrap_err("Couldn't get manifest path")?;
Expand Down Expand Up @@ -587,15 +610,19 @@ pub(crate) fn format_display_path(path: &Path) -> eyre::Result<String> {
Ok(out)
}

fn filter_contents(manifest_path: &Path, mut input: String) -> eyre::Result<String> {
fn filter_contents(
manifest_path: &Path,
mut input: String,
cargo_flags: &[String],
) -> eyre::Result<String> {
if input.contains("@GIT_HASH@") {
// avoid doing this if we don't actually have the token
// the project might not be a git repo so running `git`
// would fail
input = input.replace("@GIT_HASH@", &get_git_hash(manifest_path)?);
}

input = input.replace("@CARGO_VERSION@", &get_version(manifest_path)?);
input = input.replace("@CARGO_VERSION@", &get_version(manifest_path, cargo_flags)?);

Ok(input)
}
Expand Down
12 changes: 10 additions & 2 deletions cargo-pgrx/src/command/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,20 @@ pub(crate) struct Package {
pub(crate) features: clap_cargo::Features,
#[clap(long)]
pub(crate) target: Option<String>,
/// Extra cargo flags forwarded to every `cargo` invocation. Repeatable and split on whitespace: `--cargo=--config=foo` or `--cargo "--offline --frozen"`.
#[clap(long = "cargo", value_name = "FLAG", allow_hyphen_values = true)]
pub(crate) cargo: Vec<String>,
#[clap(from_global, action = ArgAction::Count)]
pub(crate) verbose: u8,
}

impl Package {
pub(crate) fn perform(mut self) -> eyre::Result<(PathBuf, Vec<PathBuf>)> {
warn_if_pg_bench_enabled(&self.features, "package");
let metadata = crate::metadata::metadata(&self.features, self.manifest_path.as_deref())
.wrap_err("couldn't get cargo metadata")?;
let cargo_flags = std::mem::take(&mut self.cargo);
let metadata =
crate::metadata::metadata(&self.features, self.manifest_path.as_deref(), &cargo_flags)
.wrap_err("couldn't get cargo metadata")?;
crate::metadata::validate(self.manifest_path.as_deref(), &metadata)?;
let package_manifest_path =
crate::manifest::manifest_path(&metadata, self.package.as_deref())
Expand Down Expand Up @@ -96,6 +101,7 @@ impl Package {
self.test,
&self.features,
self.target.as_deref(),
&cargo_flags,
)?;

Ok((out_dir, output_files))
Expand Down Expand Up @@ -125,6 +131,7 @@ pub(crate) fn package_extension(
is_test: bool,
features: &clap_cargo::Features,
target: Option<&str>,
cargo_flags: &[String],
) -> eyre::Result<Vec<PathBuf>> {
let out_dir_exists = out_dir.try_exists().wrap_err_with(|| {
format!("failed to access {} while packaging extension", out_dir.display())
Expand All @@ -144,6 +151,7 @@ pub(crate) fn package_extension(
Some(out_dir),
features,
target,
cargo_flags,
)
}

Expand Down
Loading
Loading