Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cargo-pgx cross compilation support #981

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
51 changes: 51 additions & 0 deletions cargo-pgx/src/command/cross_options.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
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<String>,
/// Cross-compilation sysroot
#[clap(long)]
pub(crate) sysroot: Option<PathBuf>,
/// Host sysroot
#[clap(long)]
pub(crate) host_sysroot: Option<PathBuf>,
/// Host pg_config
#[clap(long)]
pub(crate) host_pg_config: Option<PathBuf>,
}

impl CrossBuildArgs {
pub(crate) fn is_cross_compiling(&self) -> bool {
self.target.is_some()
}

pub(crate) fn to_build(&self) -> CrossBuild {
match self.is_cross_compiling() {
true => CrossBuild::Target {
target: self.target.clone().unwrap(),
sysroot: self.sysroot.clone(),
},
false => CrossBuild::None,
}
}

pub(crate) fn to_host_build(&self) -> CrossBuild {
match self.is_cross_compiling() {
jaskij marked this conversation as resolved.
Show resolved Hide resolved
true => CrossBuild::Host {
sysroot: self.host_sysroot.clone(),
pg_config: self.host_pg_config.clone(),
},
false => CrossBuild::None,
}
}
}

#[derive(Debug)]
pub(crate) enum CrossBuild {
None,
jaskij marked this conversation as resolved.
Show resolved Hide resolved
Target { target: String, sysroot: Option<PathBuf> },
Host { sysroot: Option<PathBuf>, pg_config: Option<PathBuf> },
}
66 changes: 63 additions & 3 deletions cargo-pgx/src/command/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -84,6 +85,7 @@ impl CommandExecute for Install {
package_manifest_path,
&pg_config,
&profile,
&CrossBuildArgs::default(),
self.test,
None,
&self.features,
Expand All @@ -104,6 +106,7 @@ pub(crate) fn install_extension(
package_manifest_path: impl AsRef<Path>,
pg_config: &PgConfig,
profile: &CargoProfile,
cross_args: &CrossBuildArgs,
is_test: bool,
base_directory: Option<PathBuf>,
features: &clap_cargo::Features,
Expand All @@ -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);
Expand Down Expand Up @@ -179,6 +187,7 @@ pub(crate) fn install_extension(
&package_manifest_path,
pg_config,
profile,
cross_args,
is_test,
features,
&extdir,
Expand Down Expand Up @@ -224,10 +233,11 @@ fn copy_file(
}

pub(crate) fn build_extension(
user_manifest_path: Option<impl AsRef<Path>>,
user_manifest_path: &Option<impl AsRef<Path>>,
user_package: Option<&String>,
profile: &CargoProfile,
features: &clap_cargo::Features,
cross_options: &CrossBuild,
) -> eyre::Result<std::process::Output> {
let flags = std::env::var("PGX_BUILD_FLAGS").unwrap_or_default();

Expand Down Expand Up @@ -259,6 +269,54 @@ pub(crate) fn build_extension(
command.arg("--all-features");
}

fn apply_sysroot(command: &mut Command, sysroot: &Option<PathBuf>) -> eyre::Result<()> {
if let Some(ref sysroot) = sysroot {
jaskij marked this conversation as resolved.
Show resolved Hide resolved
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 {
CrossBuild::None | CrossBuild::Target { .. } => {
if let Some(pg_config) = std::env::var_os("PGX_PG_CONFIG_PATH") {
command.env("PG_CONFIG", pg_config);
}
}
CrossBuild::Host { pg_config, .. } => {
if let Some(ref host_pg_config) = pg_config {
command.env("PGX_PG_CONFIG_PATH", host_pg_config);
command.env("PF_CONFIG", host_pg_config);
jaskij marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

// sysroot and target handling
match cross_options {
CrossBuild::None => {}
CrossBuild::Target { target, sysroot } => {
command.arg("--target");
command.arg(target);

apply_sysroot(&mut command, &sysroot)?;
}
CrossBuild::Host { sysroot, .. } => {
let var_names = vec!["CC", "LD", "CFLAGS", "LDFLAGS"];
jaskij marked this conversation as resolved.
Show resolved Hide resolved
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);
}
}

apply_sysroot(&mut command, &sysroot)?;
}
}

command.arg("--message-format=json-render-diagnostics");

for arg in flags.split_ascii_whitespace() {
Expand Down Expand Up @@ -300,6 +358,7 @@ fn copy_sql_files(
package_manifest_path: impl AsRef<Path>,
pg_config: &PgConfig,
profile: &CargoProfile,
cross_args: &CrossBuildArgs,
is_test: bool,
features: &clap_cargo::Features,
extdir: &PathBuf,
Expand All @@ -315,6 +374,7 @@ fn copy_sql_files(
user_package,
&package_manifest_path,
profile,
cross_args,
is_test,
features,
Some(&dest),
Expand Down
1 change: 1 addition & 0 deletions cargo-pgx/src/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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_options;
pub(crate) mod get;
pub(crate) mod init;
pub(crate) mod install;
Expand Down
10 changes: 8 additions & 2 deletions cargo-pgx/src/command/package.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -32,6 +33,8 @@ pub(crate) struct Package {
/// Specific profile to use (conflicts with `--debug`)
#[clap(long)]
profile: Option<String>,
#[command(flatten)]
cross_args: CrossBuildArgs,
/// Build in test mode (for `cargo pgx test`)
#[clap(long)]
test: bool,
Expand Down Expand Up @@ -89,15 +92,16 @@ impl CommandExecute for Package {
&pg_config,
out_dir,
&profile,
&self.cross_args,
self.test,
&self.features,
)
}
}

#[tracing::instrument(level = "error", skip_all, fields(
pg_version = %pg_config.version()?,
profile = ?profile,
pg_version = % pg_config.version() ?,
jaskij marked this conversation as resolved.
Show resolved Hide resolved
profile = ? profile,
test = is_test,
))]
pub(crate) fn package_extension(
Expand All @@ -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<()> {
Expand All @@ -121,6 +126,7 @@ pub(crate) fn package_extension(
&package_manifest_path,
pg_config,
profile,
cross_args,
is_test,
Some(out_dir),
features,
Expand Down
2 changes: 2 additions & 0 deletions cargo-pgx/src/command/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -116,6 +117,7 @@ pub(crate) fn run(
package_manifest_path,
pg_config,
profile,
&CrossBuildArgs::default(),
false,
None,
features,
Expand Down
32 changes: 28 additions & 4 deletions cargo-pgx/src/command/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -183,6 +187,7 @@ pub(crate) fn generate_schema(
user_package: Option<&String>,
package_manifest_path: impl AsRef<Path>,
profile: &CargoProfile,
cross_args: &CrossBuildArgs,
is_test: bool,
features: &clap_cargo::Features,
path: Option<impl AsRef<std::path::Path>>,
Expand Down Expand Up @@ -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(ref user_manifest_path) = user_manifest_path {
jaskij marked this conversation as resolved.
Show resolved Hide resolved
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);
}
Expand Down Expand Up @@ -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();
Expand All @@ -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")?;
Expand Down Expand Up @@ -480,6 +501,7 @@ pub(crate) fn generate_schema(
fn create_stub(
postmaster_path: impl AsRef<Path>,
postmaster_stub_dir: impl AsRef<Path>,
cross_build: bool,
) -> eyre::Result<PathBuf> {
let postmaster_path = postmaster_path.as_ref();
let postmaster_stub_dir = postmaster_stub_dir.as_ref();
Expand Down Expand Up @@ -538,7 +560,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_RUSTFLAG" };
jaskij marked this conversation as resolved.
Show resolved Hide resolved

if let Some(rustc_flags_str) = std::env::var(rustflags_var_name).ok() {
let rustc_flags = rustc_flags_str.split(' ').collect::<Vec<_>>();
so_rustc_invocation.args(rustc_flags);
}
Expand Down