Skip to main content

mysql_handler_build/
lib.rs

1// Copyright (C) 2026 ren-yamanashi
2//
3// This program is free software; you can redistribute it and/or modify
4// it under the terms of the GNU General Public License, version 2.0,
5// as published by the Free Software Foundation.
6//
7// This program is designed to work with certain software (including
8// but not limited to OpenSSL) that is licensed under separate terms,
9// as designated in a particular file or component or in included license
10// documentation. The authors of this program hereby grant you an additional
11// permission to link the program and your derivative works with the
12// separately licensed software that they have either included with
13// the program or referenced in the documentation.
14//
15// This program is distributed in the hope that it will be useful,
16// but WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with this program; if not, see <https://www.gnu.org/licenses/>.
22
23//! Build-script helper for `mysql-handler` downstream engine cdylibs.
24//!
25//! A downstream engine crate's `build.rs` reduces to a single call to
26//! [`configure`]. The helper wires the C++ shim staticlib and the
27//! platform's C++ runtime into the link line so the resulting cdylib
28//! is loadable by mysqld.
29
30const MISSING_SHIM_WARNING: &str = "cargo:warning=mysql-handler shim staticlib not produced — the resulting cdylib will be missing C++ shim symbols and cannot be loaded by mysqld. Set MYSQL_HANDLER_FROM_SOURCE=1 or MYSQL_HANDLER_ARCHIVE=<path> to enable shim linking.";
31
32/// Configure the link line for a `mysql-handler` engine cdylib.
33///
34/// Call this from `build.rs`. The helper performs three steps:
35///
36/// 1. **Shim staticlib.** When the `mysql-handler` crate's build script
37///    produced `libha_rusty_shim.a`, it exports the output directory via
38///    `DEP_HA_RUSTY_SHIM_STATICLIB_DIR` (Cargo's `links` metadata
39///    bridge). The helper emits `rustc-link-search` and
40///    `rustc-link-lib=static=ha_rusty_shim` so the cdylib picks the
41///    staticlib up.
42/// 2. **C++ runtime.** On Apple targets the helper links `c++`; on
43///    Linux, `stdc++`. Other targets are skipped — engines targeting
44///    them must wire their own runtime.
45/// 3. **macOS dynamic lookup.** On Apple targets, the helper adds
46///    `-Wl,-undefined,dynamic_lookup` so the C++ symbols defined inside
47///    mysqld (the loading host) are resolved at plugin-load time rather
48///    than at link time.
49///
50/// When `DEP_HA_RUSTY_SHIM_STATICLIB_DIR` is unset — that is, the user
51/// has not opted into `MYSQL_HANDLER_FROM_SOURCE=1` or
52/// `MYSQL_HANDLER_ARCHIVE=<path>` for the `mysql-handler` dependency —
53/// the helper emits a `cargo:warning=` describing the env vars to set.
54/// The build is not failed; `cargo check` and IDE workflows still work,
55/// but the produced cdylib will not load into mysqld.
56///
57/// # Panics
58///
59/// Panics if Cargo did not export `TARGET`. Cargo always exports it
60/// before invoking a build script, so this branch is unreachable in
61/// practice.
62pub fn configure() {
63    let target = std::env::var("TARGET").expect("TARGET is always set by cargo");
64    let shim_dir = std::env::var("DEP_HA_RUSTY_SHIM_STATICLIB_DIR").ok();
65
66    for line in directives(&target, shim_dir.as_deref()) {
67        println!("{line}");
68    }
69}
70
71fn directives(target: &str, shim_dir: Option<&str>) -> Vec<String> {
72    let mut out = Vec::new();
73    match shim_dir {
74        Some(dir) => {
75            out.push(format!("cargo:rustc-link-search=native={dir}"));
76            out.push("cargo:rustc-link-lib=static=ha_rusty_shim".into());
77        }
78        None => out.push(MISSING_SHIM_WARNING.into()),
79    }
80
81    if target.contains("apple") {
82        out.push("cargo:rustc-link-lib=dylib=c++".into());
83        out.push("cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup".into());
84    } else if target.contains("linux") {
85        out.push("cargo:rustc-link-lib=dylib=stdc++".into());
86    }
87
88    out
89}
90
91#[cfg(test)]
92mod tests {
93    use super::{MISSING_SHIM_WARNING, directives};
94
95    #[test]
96    fn apple_with_shim_dir_emits_static_cpp_and_dynamic_lookup() {
97        let lines = directives("aarch64-apple-darwin", Some("/tmp/shim"));
98        assert!(
99            lines
100                .iter()
101                .any(|l| l == "cargo:rustc-link-search=native=/tmp/shim")
102        );
103        assert!(
104            lines
105                .iter()
106                .any(|l| l == "cargo:rustc-link-lib=static=ha_rusty_shim")
107        );
108        assert!(lines.iter().any(|l| l == "cargo:rustc-link-lib=dylib=c++"));
109        assert!(
110            lines
111                .iter()
112                .any(|l| l == "cargo:rustc-link-arg=-Wl,-undefined,dynamic_lookup")
113        );
114        assert!(!lines.iter().any(|l| l.starts_with("cargo:warning=")));
115    }
116
117    #[test]
118    fn linux_with_shim_dir_emits_static_and_stdcpp() {
119        let lines = directives("x86_64-unknown-linux-gnu", Some("/var/cache/shim"));
120        assert!(
121            lines
122                .iter()
123                .any(|l| l == "cargo:rustc-link-search=native=/var/cache/shim")
124        );
125        assert!(
126            lines
127                .iter()
128                .any(|l| l == "cargo:rustc-link-lib=static=ha_rusty_shim")
129        );
130        assert!(
131            lines
132                .iter()
133                .any(|l| l == "cargo:rustc-link-lib=dylib=stdc++")
134        );
135        assert!(!lines.iter().any(|l| l.contains("dynamic_lookup")));
136        assert!(!lines.iter().any(|l| l.starts_with("cargo:warning=")));
137    }
138
139    #[test]
140    fn missing_shim_dir_emits_warning_and_no_static_link() {
141        let lines = directives("aarch64-apple-darwin", None);
142        assert!(lines.iter().any(|l| l == MISSING_SHIM_WARNING));
143        assert!(
144            !lines
145                .iter()
146                .any(|l| l.starts_with("cargo:rustc-link-search"))
147        );
148        assert!(!lines.iter().any(|l| l.contains("static=ha_rusty_shim")));
149        // C++ runtime still wired so cargo check produces sensible output.
150        assert!(lines.iter().any(|l| l == "cargo:rustc-link-lib=dylib=c++"));
151    }
152
153    #[test]
154    fn unknown_target_skips_cpp_runtime() {
155        let lines = directives("wasm32-unknown-unknown", Some("/x"));
156        assert!(!lines.iter().any(|l| l.contains("c++")));
157        assert!(!lines.iter().any(|l| l.contains("stdc++")));
158        assert!(!lines.iter().any(|l| l.contains("dynamic_lookup")));
159    }
160}