diff --git a/cargo-pgx/src/command/cross_options.rs b/cargo-pgx/src/command/cross_options.rs new file mode 100644 index 0000000000..93072b5b03 --- /dev/null +++ b/cargo-pgx/src/command/cross_options.rs @@ -0,0 +1,52 @@ +use clap::Args; +use std::path::PathBuf; + +#[derive(Args, Clone, Default, Debug)] +pub(crate) struct CrossBuildArgs { + /// Cross-compilation target - passing this option will make cargo-pgx assume we are cross-compiling + #[clap(long)] + pub(crate) target: Option, + /// Cross-compilation sysroot + #[clap(long)] + pub(crate) sysroot: Option, + /// Host sysroot + #[clap(long)] + pub(crate) host_sysroot: Option, + /// Host pg_config + #[clap(long)] + pub(crate) host_pg_config: Option, +} + +impl CrossBuildArgs { + pub(crate) fn is_cross_compiling(&self) -> bool { + self.target.is_some() + } + + pub(crate) fn to_build(&self) -> Option { + if self.is_cross_compiling() { + Some(CrossBuild::Target { + target: self.target.clone().unwrap(), + sysroot: self.sysroot.clone(), + }) + } else { + None + } + } + + pub(crate) fn to_host_build(&self) -> Option { + if self.is_cross_compiling() { + Some(CrossBuild::Host { + sysroot: self.host_sysroot.clone(), + pg_config: self.host_pg_config.clone(), + }) + } else { + None + } + } +} + +#[derive(Debug)] +pub(crate) enum CrossBuild { + Target { target: String, sysroot: Option }, + Host { sysroot: Option, pg_config: Option }, +} diff --git a/cargo-pgx/src/command/install.rs b/cargo-pgx/src/command/install.rs index 99d497c6a6..c8a3cda481 100644 --- a/cargo-pgx/src/command/install.rs +++ b/cargo-pgx/src/command/install.rs @@ -7,6 +7,7 @@ All rights reserved. Use of this source code is governed by the MIT license that can be found in the LICENSE file. */ +use crate::command::cross_options::{CrossBuild, CrossBuildArgs}; use crate::command::get::{find_control_file, get_property}; use crate::manifest::{display_version_info, PgVersionSource}; use crate::profile::CargoProfile; @@ -84,6 +85,7 @@ impl CommandExecute for Install { package_manifest_path, &pg_config, &profile, + &CrossBuildArgs::default(), self.test, None, &self.features, @@ -104,6 +106,7 @@ pub(crate) fn install_extension( package_manifest_path: impl AsRef, pg_config: &PgConfig, profile: &CargoProfile, + cross_args: &CrossBuildArgs, is_test: bool, base_directory: Option, features: &clap_cargo::Features, @@ -124,8 +127,13 @@ pub(crate) fn install_extension( let versioned_so = get_property(&package_manifest_path, "module_pathname")?.is_none(); - let build_command_output = - build_extension(user_manifest_path.as_ref(), user_package, &profile, &features)?; + let build_command_output = build_extension( + &user_manifest_path.as_ref(), + user_package, + &profile, + &features, + &cross_args.to_build(), + )?; let build_command_bytes = build_command_output.stdout; let build_command_reader = BufReader::new(build_command_bytes.as_slice()); let build_command_stream = cargo_metadata::Message::parse_stream(build_command_reader); @@ -179,6 +187,7 @@ pub(crate) fn install_extension( &package_manifest_path, pg_config, profile, + cross_args, is_test, features, &extdir, @@ -224,10 +233,11 @@ fn copy_file( } pub(crate) fn build_extension( - user_manifest_path: Option>, + user_manifest_path: &Option>, user_package: Option<&String>, profile: &CargoProfile, features: &clap_cargo::Features, + cross_options: &Option, ) -> eyre::Result { let flags = std::env::var("PGX_BUILD_FLAGS").unwrap_or_default(); @@ -259,6 +269,56 @@ pub(crate) fn build_extension( command.arg("--all-features"); } + fn apply_sysroot(command: &mut Command, sysroot: &Option) -> eyre::Result<()> { + if let Some(sysroot) = sysroot { + let sysroot_str = sysroot.to_str().ok_or(eyre!("sysroot is not valid utf-8"))?; + let sysroot_arg = format!("--sysroot={sysroot_str}"); + command.env("BINDGEN_EXTRA_CLANG_ARGS", &sysroot_arg); + command.env("PG_CFLAGS", &sysroot_arg); + } + + Ok(()) + } + + // set PG_CONFIG + match cross_options { + None | Some(CrossBuild::Target { .. }) => { + if let Some(pg_config) = std::env::var_os("PGX_PG_CONFIG_PATH") { + command.env("PG_CONFIG", pg_config); + } + } + Some(CrossBuild::Host { pg_config, .. }) => { + if let Some(ref host_pg_config) = pg_config { + command.env("PGX_PG_CONFIG_PATH", host_pg_config); + command.env("PG_CONFIG", host_pg_config); + } + } + } + + // sysroot and target handling + match cross_options { + None => {} + Some(CrossBuild::Target { target, sysroot }) => { + command.arg("--target"); + command.arg(target); + + apply_sysroot(&mut command, &sysroot)?; + } + Some(CrossBuild::Host { sysroot, .. }) => { + let var_names = vec!["CC", "LD", "CFLAGS", "LDFLAGS", "AR", "RUSTFLAGS"]; + for var in var_names { + let host_v = "HOST_".to_owned() + var; + if let Some(value) = std::env::var_os(host_v) { + command.env(var, value); + } else { + command.env_remove(var); + } + } + + apply_sysroot(&mut command, &sysroot)?; + } + } + command.arg("--message-format=json-render-diagnostics"); for arg in flags.split_ascii_whitespace() { @@ -300,6 +360,7 @@ fn copy_sql_files( package_manifest_path: impl AsRef, pg_config: &PgConfig, profile: &CargoProfile, + cross_args: &CrossBuildArgs, is_test: bool, features: &clap_cargo::Features, extdir: &PathBuf, @@ -315,6 +376,7 @@ fn copy_sql_files( user_package, &package_manifest_path, profile, + cross_args, is_test, features, Some(&dest), diff --git a/cargo-pgx/src/command/mod.rs b/cargo-pgx/src/command/mod.rs index b8e0141048..5e0e292cf5 100644 --- a/cargo-pgx/src/command/mod.rs +++ b/cargo-pgx/src/command/mod.rs @@ -9,6 +9,7 @@ Use of this source code is governed by the MIT license that can be found in the pub(crate) mod connect; pub(crate) mod cross; +pub(crate) mod cross_options; pub(crate) mod get; pub(crate) mod init; pub(crate) mod install; diff --git a/cargo-pgx/src/command/package.rs b/cargo-pgx/src/command/package.rs index 6cde31d601..e66a7deacd 100644 --- a/cargo-pgx/src/command/package.rs +++ b/cargo-pgx/src/command/package.rs @@ -7,6 +7,7 @@ All rights reserved. Use of this source code is governed by the MIT license that can be found in the LICENSE file. */ +use crate::command::cross_options::CrossBuildArgs; use crate::command::install::install_extension; use crate::manifest::{display_version_info, PgVersionSource}; use crate::CommandExecute; @@ -32,6 +33,8 @@ pub(crate) struct Package { /// Specific profile to use (conflicts with `--debug`) #[clap(long)] profile: Option, + #[command(flatten)] + cross_args: CrossBuildArgs, /// Build in test mode (for `cargo pgx test`) #[clap(long)] test: bool, @@ -89,6 +92,7 @@ impl CommandExecute for Package { &pg_config, out_dir, &profile, + &self.cross_args, self.test, &self.features, ) @@ -107,6 +111,7 @@ pub(crate) fn package_extension( pg_config: &PgConfig, out_dir: PathBuf, profile: &CargoProfile, + cross_args: &CrossBuildArgs, is_test: bool, features: &clap_cargo::Features, ) -> eyre::Result<()> { @@ -121,6 +126,7 @@ pub(crate) fn package_extension( &package_manifest_path, pg_config, profile, + cross_args, is_test, Some(out_dir), features, diff --git a/cargo-pgx/src/command/run.rs b/cargo-pgx/src/command/run.rs index 68484a0a0b..6b9676cb22 100644 --- a/cargo-pgx/src/command/run.rs +++ b/cargo-pgx/src/command/run.rs @@ -7,6 +7,7 @@ All rights reserved. Use of this source code is governed by the MIT license that can be found in the LICENSE file. */ +use crate::command::cross_options::CrossBuildArgs; use crate::command::get::get_property; use crate::command::install::install_extension; use crate::command::start::start_postgres; @@ -116,6 +117,7 @@ pub(crate) fn run( package_manifest_path, pg_config, profile, + &CrossBuildArgs::default(), false, None, features, diff --git a/cargo-pgx/src/command/schema.rs b/cargo-pgx/src/command/schema.rs index 3cb124011e..6ac9ae91c6 100644 --- a/cargo-pgx/src/command/schema.rs +++ b/cargo-pgx/src/command/schema.rs @@ -7,7 +7,7 @@ All rights reserved. Use of this source code is governed by the MIT license that can be found in the LICENSE file. */ use crate::command::get::{find_control_file, get_property}; -use crate::command::install::format_display_path; +use crate::command::install::{build_extension, format_display_path}; use crate::pgx_pg_sys_stub::PgxPgSysStub; use crate::profile::CargoProfile; use crate::CommandExecute; @@ -23,8 +23,11 @@ use std::path::{Path, PathBuf}; use std::process::{Command, Stdio}; // Since we support extensions with `#[no_std]` extern crate alloc; +use crate::command::cross_options::CrossBuildArgs; use crate::manifest::{get_package_manifest, pg_config_and_version}; use alloc::vec::Vec; +use std::env; +use std::ffi::OsString; // An apparent bug in `glibc` 2.17 prevents us from safely dropping this // otherwise users find issues such as https://github.com/tcdi/pgx/issues/572 @@ -112,6 +115,7 @@ impl CommandExecute for Schema { self.package.as_ref(), package_manifest_path, &profile, + &CrossBuildArgs::default(), self.test, &self.features, self.out.as_ref(), @@ -183,6 +187,7 @@ pub(crate) fn generate_schema( user_package: Option<&String>, package_manifest_path: impl AsRef, profile: &CargoProfile, + cross_args: &CrossBuildArgs, is_test: bool, features: &clap_cargo::Features, path: Option>, @@ -230,13 +235,17 @@ pub(crate) fn generate_schema( command.arg(user_package); } - if let Some(user_manifest_path) = user_manifest_path { + if let Some(user_manifest_path) = &user_manifest_path { command.arg("--manifest-path"); command.arg(user_manifest_path.as_ref()); } command.args(profile.cargo_args()); + if cross_args.is_cross_compiling() { + command.env("RUSTFLAGS", env::var_os("HOST_RUSTFLAGS").unwrap_or(OsString::new())); + } + if let Some(log_level) = &log_level { command.env("RUST_LOG", log_level); } @@ -297,7 +306,8 @@ pub(crate) fn generate_schema( // The next action may take a few seconds, we'd like the user to know we're thinking. eprintln!("{} SQL entities", " Discovering".bold().green(),); - let postmaster_stub_built = create_stub(&postmaster_path, &postmaster_stub_dir)?; + let postmaster_stub_built = + create_stub(&postmaster_path, &postmaster_stub_dir, cross_args.is_cross_compiling())?; // Inspect the symbol table for a list of `__pgx_internals` we should have the generator call let mut lib_so = target_dir_with_profile.clone(); @@ -306,6 +316,17 @@ pub(crate) fn generate_schema( lib_so.push(&format!("lib{}{}", package_name.replace('-', "_"), so_extension)); + if cross_args.is_cross_compiling() && !lib_so.try_exists()? { + eprintln!("Native plugin doesn't exist, building"); + build_extension( + &user_manifest_path, + user_package, + profile, + features, + &cross_args.to_host_build(), + )?; + } + let lib_so_data = std::fs::read(&lib_so).wrap_err("couldn't read extension shared object")?; let lib_so_obj_file = object::File::parse(&*lib_so_data).wrap_err("couldn't parse extension shared object")?; @@ -471,6 +492,7 @@ pub(crate) fn generate_schema( fn create_stub( postmaster_path: impl AsRef, postmaster_stub_dir: impl AsRef, + cross_build: bool, ) -> eyre::Result { let postmaster_path = postmaster_path.as_ref(); let postmaster_stub_dir = postmaster_stub_dir.as_ref(); @@ -529,7 +551,9 @@ fn create_stub( let mut so_rustc_invocation = Command::new("rustc"); so_rustc_invocation.stderr(Stdio::inherit()); - if let Some(rustc_flags_str) = std::env::var("RUSTFLAGS").ok() { + let rustflags_var_name = if cross_build { "RUSTFLAGS" } else { "HOST_RUSTFLAGS" }; + + if let Some(rustc_flags_str) = std::env::var(rustflags_var_name).ok() { let rustc_flags = rustc_flags_str.split(' ').collect::>(); so_rustc_invocation.args(rustc_flags); } diff --git a/pgx-pg-sys/build.rs b/pgx-pg-sys/build.rs index dca68af989..8179ba9141 100644 --- a/pgx-pg-sys/build.rs +++ b/pgx-pg-sys/build.rs @@ -831,6 +831,7 @@ fn build_shim_for_version( Command::new(make) .arg("clean") .arg(&format!("libpgx-cshim-{}.a", major_version)) + .args(pg_config.path().map(|p| format!("PG_CONFIG={}", p.display()))) .env("PG_TARGET_VERSION", format!("{}", major_version)) .env("PATH", path_env) .current_dir(shim_dst), diff --git a/pgx-pg-sys/cshim/Makefile b/pgx-pg-sys/cshim/Makefile index cc0a7351a9..94902af5ee 100644 --- a/pgx-pg-sys/cshim/Makefile +++ b/pgx-pg-sys/cshim/Makefile @@ -21,6 +21,6 @@ all: ${STATIC_LIB_NAME} EXTRA_CLEAN += ${STATIC_LIB_NAME} -PG_CONFIG = pg_config +PG_CONFIG ?= pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS)