use crate::CommandExecute;
use crate::cargo::CargoProfile;
use crate::command::get::get_property;
use crate::command::install::{install_extension, warn_if_pg_bench_enabled};
use crate::manifest::{PgVersionSource, display_version_info};
use cargo_toml::Manifest;
use eyre::{WrapErr, eyre};
use pgrx_pg_config::{PgConfig, Pgrx, get_target_dir};
use std::path::{Path, PathBuf};
#[derive(clap::Args, Debug)]
#[clap(author)]
pub(crate) struct Package {
#[clap(long, short)]
pub(crate) package: Option<String>,
#[clap(long, value_parser)]
pub(crate) manifest_path: Option<PathBuf>,
#[clap(long, short)]
pub(crate) debug: bool,
#[clap(long)]
pub(crate) profile: Option<String>,
#[clap(long)]
pub(crate) test: bool,
#[clap(long, short = 'c', value_parser)]
pub(crate) pg_config: Option<PathBuf>,
#[clap(long, value_parser)]
pub(crate) out_dir: Option<PathBuf>,
#[clap(flatten)]
pub(crate) features: clap_cargo::Features,
#[clap(long)]
pub(crate) target: Option<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")?;
crate::metadata::validate(self.manifest_path.as_deref(), &metadata)?;
let package_manifest_path =
crate::manifest::manifest_path(&metadata, self.package.as_deref())
.wrap_err("Couldn't get manifest path")?;
let package_manifest =
Manifest::from_path(&package_manifest_path).wrap_err("Couldn't parse manifest")?;
let pg_config = match self.pg_config {
None => PgConfig::from_path(),
Some(config) => PgConfig::new_with_defaults(config),
};
let pg_version = format!("pg{}", pg_config.major_version()?);
crate::manifest::modify_features_for_version(
&Pgrx::from_config()?,
Some(&mut self.features),
&package_manifest,
&PgVersionSource::PgConfig(pg_version),
false,
);
let profile = CargoProfile::from_flags(
self.profile.as_deref(),
if self.debug { CargoProfile::Dev } else { CargoProfile::Release },
)?;
let out_dir = if let Some(out_dir) = self.out_dir {
out_dir
} else {
build_base_path(&pg_config, &package_manifest_path, &profile, self.target.as_deref())?
};
let output_files = package_extension(
self.manifest_path.as_deref(),
self.package.as_deref(),
&package_manifest_path,
&pg_config,
out_dir.clone(),
&profile,
self.test,
&self.features,
self.target.as_deref(),
)?;
Ok((out_dir, output_files))
}
}
impl CommandExecute for Package {
#[tracing::instrument(level = "error", skip(self))]
fn execute(self) -> eyre::Result<()> {
self.perform()?;
Ok(())
}
}
#[tracing::instrument(level = "error", skip_all, fields(
pg_version = %pg_config.version()?,
profile = ?profile,
test = is_test,
))]
pub(crate) fn package_extension(
user_manifest_path: Option<&Path>,
user_package: Option<&str>,
package_manifest_path: &Path,
pg_config: &PgConfig,
out_dir: PathBuf,
profile: &CargoProfile,
is_test: bool,
features: &clap_cargo::Features,
target: Option<&str>,
) -> 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())
})?;
if !out_dir_exists {
std::fs::create_dir_all(&out_dir)?;
}
display_version_info(pg_config, &PgVersionSource::PgConfig(pg_config.label()?));
install_extension(
user_manifest_path,
user_package,
package_manifest_path,
pg_config,
profile,
is_test,
Some(out_dir),
features,
target,
)
}
pub(crate) fn build_base_path(
pg_config: &PgConfig,
manifest_path: &Path,
profile: &CargoProfile,
target: Option<&str>,
) -> eyre::Result<PathBuf> {
let mut target_dir = get_target_dir()?;
let pgver = pg_config.major_version()?;
let extname = get_property(manifest_path, "extname")?
.ok_or(eyre!("could not determine extension name"))?;
if let Some(target) = target {
target_dir.push(target);
}
target_dir.push(profile.target_subdir());
target_dir.push(format!("{extname}-pg{pgver}"));
Ok(target_dir)
}