use super::PackageArtifact;
use crate::platform::RustTarget;
use alef_core::config::ResolvedCrateConfig;
use anyhow::Result;
use std::fs;
use std::path::Path;
pub fn package_c_ffi(
config: &ResolvedCrateConfig,
target: &RustTarget,
workspace_root: &Path,
output_dir: &Path,
version: &str,
) -> Result<PackageArtifact> {
let lib_name = config.ffi_lib_name();
let header_name = config.ffi_header_name();
let crate_name = &config.name;
let platform = target.platform_for(alef_core::config::extras::Language::Ffi);
let pkg_name = format!("{crate_name}-ffi-v{version}-{platform}");
let staging = output_dir.join(&pkg_name);
if staging.exists() {
fs::remove_dir_all(&staging)?;
}
let lib_dir = staging.join("lib");
let include_dir = staging.join("include");
fs::create_dir_all(&lib_dir)?;
fs::create_dir_all(&include_dir)?;
let shared_lib = target.shared_lib_name(&lib_name);
let shared_src = super::find_built_artifact(workspace_root, target, &shared_lib)?;
fs::copy(&shared_src, lib_dir.join(&shared_lib))?;
let static_lib = target.static_lib_name(&lib_name);
if let Ok(static_src) = super::find_built_artifact(workspace_root, target, &static_lib) {
fs::copy(&static_src, lib_dir.join(&static_lib))?;
}
let ffi_crate_dir = crate::ffi_stage::find_ffi_crate_dir_pub(config, workspace_root);
let header_src = ffi_crate_dir.join("include").join(&header_name);
if header_src.exists() {
fs::copy(&header_src, include_dir.join(&header_name))?;
}
let pub_config = publish_lang_config(config);
if pub_config.pkg_config.unwrap_or(true) {
let pkgconfig_dir = staging.join("share/pkgconfig");
fs::create_dir_all(&pkgconfig_dir)?;
let pc_content = generate_pc_file(crate_name, version, &lib_name, &header_name);
fs::write(pkgconfig_dir.join(format!("{crate_name}.pc")), pc_content)?;
}
if pub_config.cmake_config.unwrap_or(true) {
let cmake_dir = staging.join("lib/cmake").join(crate_name);
fs::create_dir_all(&cmake_dir)?;
let cmake_content = generate_cmake_config(crate_name, &lib_name);
fs::write(cmake_dir.join(format!("{crate_name}-config.cmake")), cmake_content)?;
let version_content = generate_cmake_version(version);
fs::write(
cmake_dir.join(format!("{crate_name}-config-version.cmake")),
version_content,
)?;
}
for name in &["LICENSE", "LICENSE-MIT", "LICENSE-APACHE"] {
let license = workspace_root.join(name);
if license.exists() {
fs::copy(&license, staging.join(name))?;
break;
}
}
let archive_name = format!("{pkg_name}.tar.gz");
let archive_path = output_dir.join(&archive_name);
super::create_tar_gz(&staging, &archive_path)?;
let _ = fs::remove_dir_all(&staging);
Ok(PackageArtifact {
path: archive_path,
name: archive_name,
checksum: None,
})
}
fn publish_lang_config(config: &ResolvedCrateConfig) -> alef_core::config::publish::PublishLanguageConfig {
if let Some(publish) = &config.publish {
if let Some(cfg) = publish.languages.get("c_ffi").or_else(|| publish.languages.get("ffi")) {
return cfg.clone();
}
}
alef_core::config::publish::PublishLanguageConfig::default()
}
fn generate_pc_file(name: &str, version: &str, lib_name: &str, _header: &str) -> String {
format!(
"prefix=${{pcfiledir}}/../..\n\
libdir=${{prefix}}/lib\n\
includedir=${{prefix}}/include\n\n\
Name: {name}\n\
Description: {name} C FFI library\n\
Version: {version}\n\
Libs: -L${{libdir}} -l{lib_name}\n\
Cflags: -I${{includedir}}\n"
)
}
fn generate_cmake_config(name: &str, lib_name: &str) -> String {
format!(
"# CMake find module for {name}\n\
get_filename_component(_dir \"${{CMAKE_CURRENT_LIST_FILE}}\" PATH)\n\
get_filename_component(_prefix \"${{_dir}}/../..\" ABSOLUTE)\n\n\
set({name}_INCLUDE_DIR \"${{_prefix}}/include\")\n\
set({name}_LIBRARY \"${{_prefix}}/lib/lib{lib_name}${{CMAKE_SHARED_LIBRARY_SUFFIX}}\")\n\n\
if(EXISTS \"${{{name}_LIBRARY}}\")\n\
\x20\x20set({name}_FOUND TRUE)\n\
else()\n\
\x20\x20set({name}_FOUND FALSE)\n\
endif()\n"
)
}
fn generate_cmake_version(version: &str) -> String {
format!(
"set(PACKAGE_VERSION \"{version}\")\n\n\
if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)\n\
\x20\x20set(PACKAGE_VERSION_EXACT TRUE)\n\
endif()\n\n\
if(NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION)\n\
\x20\x20set(PACKAGE_VERSION_COMPATIBLE TRUE)\n\
else()\n\
\x20\x20set(PACKAGE_VERSION_UNSUITABLE TRUE)\n\
endif()\n"
)
}