-
-
Notifications
You must be signed in to change notification settings - Fork 3
fix: FFI/cgo string passing #133
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
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
dd01271
fix: FFI/cgo string passing
Techassi 49dccf4
Add GOARCH and GOOS env vars to Go command
Techassi 427caff
Merge branch 'main' into fix/ffi-cgo
Techassi c035f79
Add CC env var
Techassi 2e424a1
Add LDFLAGS to cgo
Techassi 40cd805
Add libresolv
Techassi e00d8ec
Add doc comment to cstr_ptr_to_string function
Techassi b3bbe3b
Fix typo
Techassi 856063e
Merge branch 'main' into fix/ffi-cgo
Techassi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,27 +1,95 @@ | ||
| use std::env; | ||
| use std::{ | ||
| env::{self, VarError}, | ||
| path::PathBuf, | ||
| process::Command, | ||
| }; | ||
|
|
||
| use gobuild::BuildMode; | ||
| use snafu::{ResultExt, Snafu}; | ||
|
|
||
| const ENV_GO_HELM_WRAPPER: &str = "GO_HELM_WRAPPER"; | ||
| #[derive(Debug, Snafu)] | ||
| enum Error { | ||
| #[snafu(display("Failed to find env var"))] | ||
| EnvVarNotFound { source: VarError }, | ||
|
|
||
| #[snafu(display("Unsupported GOARCH: {arch}"))] | ||
| UnsupportedGoArch { arch: String }, | ||
|
|
||
| #[snafu(display("Unsupported GOOS: {os}"))] | ||
| UnsupportedGoOs { os: String }, | ||
| } | ||
|
|
||
| fn main() { | ||
| // cgo requires an explicit dependency on libresolv on some platforms (such as Red Hat Enterprise Linux 8 and derivatives) | ||
| let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); | ||
|
|
||
| println!("cargo:rerun-if-changed=go-helm-wrapper/main.go"); | ||
|
|
||
| let cc = cc::Build::new().try_get_compiler().unwrap(); | ||
| let goarch = get_goarch().unwrap(); | ||
| let goos = get_goos().unwrap(); | ||
|
|
||
| let mut cmd = Command::new("go"); | ||
| cmd.arg("build") | ||
| .args(["-buildmode", "c-archive"]) | ||
| .arg("-o") | ||
| .arg(out_path.join("libgo-helm-wrapper.a")) | ||
| .arg("go-helm-wrapper/main.go") | ||
| .env("CGO_ENABLED", "1") | ||
| .env("GOARCH", goarch) | ||
| .env("GOOS", goos) | ||
| .env("CC", format!("'{}'", cc.path().display())); | ||
|
|
||
| cmd.status().expect("Failed to build go-helm-wrapper"); | ||
|
|
||
| let bindings = bindgen::builder() | ||
| .header(out_path.join("libgo-helm-wrapper.h").to_str().unwrap()) | ||
| .parse_callbacks(Box::new(bindgen::CargoCallbacks)) | ||
| .generate() | ||
| .expect("Failed to generate Rust bindings from Go header file"); | ||
|
|
||
| bindings | ||
| .write_to_file(out_path.join("bindings.rs")) | ||
| .expect("Failed to write bindings"); | ||
|
|
||
| println!("cargo:rustc-link-lib=resolv"); | ||
| println!("cargo:rerun-if-env-changed={ENV_GO_HELM_WRAPPER}"); | ||
| match env::var(ENV_GO_HELM_WRAPPER) { | ||
| Ok(go_helm_wrapper) => { | ||
| // Reuse pre-built helm wrapper if possible | ||
| eprintln!("Reusing pre-built go-helm-wrapper ({go_helm_wrapper:?})"); | ||
| println!("cargo:rustc-link-lib=static:+verbatim={go_helm_wrapper}"); | ||
| } | ||
| Err(env::VarError::NotPresent) => { | ||
| gobuild::Build::new() | ||
| .file("go-helm-wrapper/main.go") | ||
| .buildmode(BuildMode::CArchive) | ||
| .compile("go-helm-wrapper"); | ||
| } | ||
| Err(err @ env::VarError::NotUnicode(..)) => { | ||
| panic!("{ENV_GO_HELM_WRAPPER} must be valid unicode: {err}"); | ||
| } | ||
| } | ||
| println!("cargo:rustc-link-lib=static=go-helm-wrapper"); | ||
| println!( | ||
| "cargo:rustc-link-search=native={}", | ||
| out_path.to_str().unwrap() | ||
| ); | ||
| } | ||
|
|
||
| fn get_goarch() -> Result<String, Error> { | ||
| let arch = env::var("CARGO_CFG_TARGET_ARCH").context(EnvVarNotFoundSnafu)?; | ||
|
|
||
| let arch = match arch.as_str() { | ||
| "x86" => "386", | ||
| "x86_64" => "amd64", | ||
| "mips" => "mips", | ||
| "powerpc" => "ppc", | ||
| "powerpc64" => "ppc64", | ||
| "arm" => "arm", | ||
| "aarch64" => "arm64", | ||
| _ => return UnsupportedGoArchSnafu { arch }.fail(), | ||
| }; | ||
|
|
||
| Ok(arch.into()) | ||
| } | ||
|
|
||
| fn get_goos() -> Result<String, Error> { | ||
| let os = env::var("CARGO_CFG_TARGET_OS").context(EnvVarNotFoundSnafu)?; | ||
|
|
||
| let os = match os.as_str() { | ||
| "windows" => "windows", | ||
| "macos" => "darwin", | ||
| "ios" => "darwin", | ||
| "linux" => "linux", | ||
| "android" => "android", | ||
| "freebsd" => "freebsd", | ||
| "dragonfly" => "dragonfly", | ||
| "openbsd" => "openbsd", | ||
| "netbsd" => "netbsd", | ||
| _ => return UnsupportedGoOsSnafu { os }.fail(), | ||
| }; | ||
|
|
||
| Ok(os.into()) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,37 +1,112 @@ | ||
| use std::{marker::PhantomData, os::raw::c_char}; | ||
| #![allow(non_upper_case_globals)] | ||
| #![allow(non_camel_case_types)] | ||
| #![allow(improper_ctypes)] | ||
| #![allow(non_snake_case)] | ||
|
|
||
| #[repr(C)] | ||
| pub struct GoString<'a> { | ||
| p: *const u8, | ||
| n: i64, | ||
| _lifetime: PhantomData<&'a str>, | ||
| include!(concat!(env!("OUT_DIR"), "/bindings.rs")); | ||
|
|
||
| use std::ffi::{c_char, CStr, CString}; | ||
|
|
||
| pub const HELM_ERROR_PREFIX: &str = "ERROR:"; | ||
|
|
||
| pub fn install_helm_release( | ||
| release_name: &str, | ||
| chart_name: &str, | ||
| chart_version: &str, | ||
| values_yaml: &str, | ||
| namespace: &str, | ||
| suppress_output: bool, | ||
| ) -> String { | ||
| let release_name = CString::new(release_name).unwrap(); | ||
| let chart_name = CString::new(chart_name).unwrap(); | ||
| let chart_version = CString::new(chart_version).unwrap(); | ||
| let values_yaml = CString::new(values_yaml).unwrap(); | ||
| let namespace = CString::new(namespace).unwrap(); | ||
|
|
||
| unsafe { | ||
| let c = go_install_helm_release( | ||
| release_name.as_ptr() as *mut c_char, | ||
| chart_name.as_ptr() as *mut c_char, | ||
| chart_version.as_ptr() as *mut c_char, | ||
| values_yaml.as_ptr() as *mut c_char, | ||
| namespace.as_ptr() as *mut c_char, | ||
| suppress_output as u8, | ||
| ); | ||
|
|
||
| cstr_ptr_to_string(c) | ||
| } | ||
| } | ||
|
|
||
| pub fn uninstall_helm_release( | ||
| release_name: &str, | ||
| namespace: &str, | ||
| suppress_output: bool, | ||
| ) -> String { | ||
| let release_name = CString::new(release_name).unwrap(); | ||
| let namespace = CString::new(namespace).unwrap(); | ||
|
|
||
| unsafe { | ||
| let c = go_uninstall_helm_release( | ||
| release_name.as_ptr() as *mut c_char, | ||
| namespace.as_ptr() as *mut c_char, | ||
| suppress_output as u8, | ||
| ); | ||
|
|
||
| cstr_ptr_to_string(c) | ||
| } | ||
| } | ||
|
|
||
| pub fn check_helm_release_exists(release_name: &str, namespace: &str) -> bool { | ||
| let release_name = CString::new(release_name).unwrap(); | ||
| let namespace = CString::new(namespace).unwrap(); | ||
|
|
||
| unsafe { | ||
| go_helm_release_exists( | ||
| release_name.as_ptr() as *mut c_char, | ||
| namespace.as_ptr() as *mut c_char, | ||
| ) != 0 | ||
| } | ||
| } | ||
|
|
||
| impl<'a> From<&'a str> for GoString<'a> { | ||
| fn from(str: &'a str) -> Self { | ||
| GoString { | ||
| p: str.as_ptr(), | ||
| n: str.len() as i64, | ||
| _lifetime: PhantomData, | ||
| } | ||
| pub fn list_helm_releases(namespace: &str) -> String { | ||
| let namespace = CString::new(namespace).unwrap(); | ||
|
|
||
| unsafe { | ||
| let c = go_helm_list_releases(namespace.as_ptr() as *mut c_char); | ||
| cstr_ptr_to_string(c) | ||
| } | ||
| } | ||
|
|
||
| extern "C" { | ||
| pub fn go_install_helm_release( | ||
| release_name: GoString, | ||
| chart_name: GoString, | ||
| chart_version: GoString, | ||
| values_yaml: GoString, | ||
| namespace: GoString, | ||
| suppress_output: bool, | ||
| ) -> *const c_char; | ||
| pub fn go_uninstall_helm_release( | ||
| release_name: GoString, | ||
| namespace: GoString, | ||
| suppress_output: bool, | ||
| ) -> *const c_char; | ||
| pub fn go_helm_release_exists(release_name: GoString, namespace: GoString) -> bool; | ||
| pub fn go_helm_list_releases(namespace: GoString) -> *const c_char; | ||
| pub fn go_add_helm_repo(name: GoString, url: GoString) -> *const c_char; | ||
| pub fn add_helm_repository(repository_name: &str, repository_url: &str) -> String { | ||
| let repository_name = CString::new(repository_name).unwrap(); | ||
| let repository_url = CString::new(repository_url).unwrap(); | ||
|
|
||
| unsafe { | ||
| let c = go_add_helm_repo( | ||
| repository_name.as_ptr() as *mut c_char, | ||
| repository_url.as_ptr() as *mut c_char, | ||
| ); | ||
|
|
||
| cstr_ptr_to_string(c) | ||
| } | ||
| } | ||
|
|
||
| /// Checks if the result string is an error, and if so, returns the error message as a string. | ||
| pub fn to_helm_error(result: &str) -> Option<String> { | ||
| if !result.is_empty() && result.starts_with(HELM_ERROR_PREFIX) { | ||
| return Some(result.replace(HELM_ERROR_PREFIX, "")); | ||
| } | ||
|
|
||
| None | ||
| } | ||
|
|
||
| /// Converts a raw C string pointer into an owned Rust [`String`]. This function | ||
| /// also makes sure, that the pointer (and underlying memory) of the Go string is | ||
| /// freed. The pointer **cannot** be used afterwards. | ||
| unsafe fn cstr_ptr_to_string(c: *mut c_char) -> String { | ||
| let cstr = CStr::from_ptr(c); | ||
| let s = String::from_utf8_lossy(cstr.to_bytes()).to_string(); | ||
| free_go_string(cstr.as_ptr() as *mut c_char); | ||
|
|
||
| s | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.