Skip to main content

pgrx_bindgen/
lib.rs

1//LICENSE Portions Copyright 2019-2021 ZomboDB, LLC.
2//LICENSE
3//LICENSE Portions Copyright 2021-2023 Technology Concepts & Design, Inc.
4//LICENSE
5//LICENSE Portions Copyright 2023-2023 PgCentral Foundation, Inc. <contact@pgcentral.org>
6//LICENSE
7//LICENSE All rights reserved.
8//LICENSE
9//LICENSE Use of this source code is governed by the MIT license that can be found in the LICENSE file.
10use eyre::eyre;
11use pgrx_pg_config::{
12    PgConfig, PgConfigSelector, Pgrx, SUPPORTED_VERSIONS, is_supported_major_version,
13};
14
15pub mod build;
16
17fn is_for_release() -> bool {
18    env_tracked("PGRX_PG_SYS_GENERATE_BINDINGS_FOR_RELEASE").as_deref() == Some("1")
19}
20
21/// Determines which `pg_config` is to be used, based on a combination of pgrx' internal knowledge
22/// of supported Postgres versions and `cargo` options/feature flags.
23pub fn detect_pg_config() -> eyre::Result<Vec<(u16, PgConfig)>> {
24    let pg_configs: Vec<(u16, PgConfig)> = if is_for_release() {
25        // This does not cross-check config.toml and Cargo.toml versions, as it is release infra.
26        Pgrx::from_config()?.iter(PgConfigSelector::All)
27            .map(|r| r.expect("invalid pg_config"))
28            .map(|c| (c.major_version().expect("invalid major version"), c))
29            .filter_map(|t| {
30                if is_supported_major_version(t.0) {
31                    Some(t)
32                } else {
33                    println!(
34                        "cargo:warning={} contains a configuration for pg{}, which pgrx does not support.",
35                        Pgrx::config_toml()
36                            .expect("Could not get PGRX configuration TOML")
37                            .to_string_lossy(),
38                        t.0
39                    );
40                    None
41                }
42            })
43            .collect()
44    } else {
45        let mut found = Vec::new();
46        for pgver in SUPPORTED_VERSIONS() {
47            if env_tracked(&format!("CARGO_FEATURE_PG{}", pgver.major)).is_some() {
48                found.push(pgver);
49            }
50        }
51        let found_ver = match &found[..] {
52            [ver] => ver,
53            [] => {
54                return Err(eyre!(
55                    "Did not find `pg$VERSION` feature. `pgrx-pg-sys` requires one of {} to be set",
56                    SUPPORTED_VERSIONS()
57                        .iter()
58                        .map(|pgver| format!("`pg{}`", pgver.major))
59                        .collect::<Vec<_>>()
60                        .join(", ")
61                ));
62            }
63            versions => {
64                return Err(eyre!(
65                    "Multiple `pg$VERSION` features found.\n`--no-default-features` may be required.\nFound: {}",
66                    versions
67                        .iter()
68                        .map(|version| format!("pg{}", version.major))
69                        .collect::<Vec<String>>()
70                        .join(", ")
71                ));
72            }
73        };
74
75        let found_major = found_ver.major;
76        if let Ok(pg_config) = PgConfig::from_env() {
77            let major_version = pg_config.major_version()?;
78
79            if major_version != found_major {
80                panic!(
81                    "Feature flag `pg{found_major}` does not match version from the environment-described PgConfig (`{major_version}`)"
82                )
83            }
84            vec![(major_version, pg_config)]
85        } else {
86            let specific = Pgrx::from_config()?.get(&format!("pg{}", found_ver.major))?;
87            vec![(found_ver.major, specific)]
88        }
89    };
90    Ok(pg_configs)
91}
92
93fn env_tracked(s: &str) -> Option<String> {
94    // a **sorted** list of environment variable keys that cargo might set that we don't need to track
95    // these were picked out, by hand, from: https://doc.rust-lang.org/cargo/reference/environment-variables.html
96    const CARGO_KEYS: &[&str] = &[
97        "BROWSER",
98        "DEBUG",
99        "DOCS_RS",
100        "HOST",
101        "HTTP_PROXY",
102        "HTTP_TIMEOUT",
103        "NUM_JOBS",
104        "OPT_LEVEL",
105        "OUT_DIR",
106        "PATH",
107        "PROFILE",
108        "TARGET",
109        "TERM",
110    ];
111
112    let is_cargo_key =
113        s.starts_with("CARGO") || s.starts_with("RUST") || CARGO_KEYS.binary_search(&s).is_ok();
114
115    if !is_cargo_key {
116        // if it's an envar that cargo gives us, we don't want to ask it to rerun build.rs if it changes
117        // we'll let cargo figure that out for itself, and doing so, depending on the key, seems to
118        // cause cargo to rerun build.rs every time, which is terrible
119        println!("cargo:rerun-if-env-changed={s}");
120    }
121    std::env::var(s).ok()
122}