Repository navigation
Expand file tree
/
Copy pathbuild.rs
More file actions
103 lines (91 loc) · 3.66 KB
/
Copy pathbuild.rs
File metadata and controls
103 lines (91 loc) · 3.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use std::env;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::process::Command;
const POSTGRES_MAJOR: u32 = 18;
const BINDINGS_FILE: &str = "pggomtm_oauth_bindings.rs";
const CALLBACK_ABI_PATTERN: &str =
"^(ValidatorStartupCB|ValidatorShutdownCB|ValidatorValidateCB|OAuthValidatorModuleInit)$";
type BuildResult<T> = Result<T, Box<dyn Error>>;
fn main() {
if env::var_os("CARGO_FEATURE_PG18").is_none() {
return;
}
println!("cargo:rerun-if-env-changed=PGRX_PG_CONFIG_PATH");
if let Err(error) = generate_bindings() {
panic!("failed to generate PostgreSQL OAuth ABI bindings: {error}");
}
}
fn generate_bindings() -> BuildResult<()> {
let pg_config = required_path("PGRX_PG_CONFIG_PATH")?;
let version = pg_config_line(&pg_config, "--version")?;
let major = version
.strip_prefix("PostgreSQL ")
.and_then(|value| value.split('.').next())
.and_then(|value| value.parse::<u32>().ok())
.ok_or("pg_config returned an unsupported version string")?;
if major != POSTGRES_MAJOR {
return Err(format!("PostgreSQL major {major} is unsupported").into());
}
let include_dir = PathBuf::from(pg_config_line(&pg_config, "--includedir-server")?);
if !include_dir.is_absolute() || !include_dir.is_dir() {
return Err("pg_config server include directory is unavailable".into());
}
let postgres_header = required_file(&include_dir.join("postgres.h"))?;
let oauth_header = required_file(&include_dir.join("libpq/oauth.h"))?;
let include_dir = utf8_path(&include_dir)?;
let postgres_header = utf8_path(&postgres_header)?;
let oauth_header = utf8_path(&oauth_header)?;
println!("cargo:rerun-if-changed={postgres_header}");
println!("cargo:rerun-if-changed={oauth_header}");
let bindings = bindgen::Builder::default()
.header(postgres_header)
.header(oauth_header)
.detect_include_paths(false)
.clang_arg(format!("-I{include_dir}"))
.allowlist_var("^PG_OAUTH_VALIDATOR_MAGIC$")
.allowlist_type(
"^(ValidatorModuleState|ValidatorModuleResult|ValidatorStartupCB|ValidatorShutdownCB|ValidatorValidateCB|OAuthValidatorCallbacks|OAuthValidatorModuleInit)$",
)
.allowlist_recursively(false)
.generate_comments(false)
.layout_tests(false)
.formatter(bindgen::Formatter::None)
.override_abi(bindgen::Abi::CUnwind, CALLBACK_ABI_PATTERN)
.generate()?;
let output = required_path("OUT_DIR")?.join(BINDINGS_FILE);
bindings.write_to_file(output)?;
Ok(())
}
fn required_path(name: &str) -> BuildResult<PathBuf> {
let path = PathBuf::from(env::var_os(name).ok_or_else(|| format!("{name} is required"))?);
if !path.is_absolute() {
return Err(format!("{name} must be absolute").into());
}
Ok(path)
}
fn required_file(path: &Path) -> BuildResult<PathBuf> {
if !path.is_file() {
return Err(format!(
"required PostgreSQL header is unavailable: {}",
path.display()
)
.into());
}
Ok(path.to_path_buf())
}
fn utf8_path(path: &Path) -> BuildResult<&str> {
path.to_str()
.ok_or_else(|| format!("path must be UTF-8: {}", path.display()).into())
}
fn pg_config_line(pg_config: &Path, argument: &str) -> BuildResult<String> {
let output = Command::new(pg_config).arg(argument).output()?;
if !output.status.success() {
return Err(format!("pg_config {argument} failed").into());
}
let value = String::from_utf8(output.stdout)?.trim().to_owned();
if value.is_empty() {
return Err(format!("pg_config {argument} returned no value").into());
}
Ok(value)
}