pub(crate) mod args;
pub(crate) mod builders;
use std::fs;
use std::fs::File;
use std::path::PathBuf;
use std::rc::Rc;
use crate::backend::*;
use crate::model::Library;
use crate::cli::args::{Args, PackageOptions};
pub fn run(settings: BindingBuilderSettings) {
let args = Args::get();
let (options, platforms) = {
let span = tracing::info_span!("configure()");
span.in_scope(|| get_platforms(&args))
};
if args.build_c {
let mut builder =
builders::c::CBindingBuilder::new(settings.clone(), platforms.cpp, &args.extra_files);
builder.run(options);
}
if args.build_dotnet {
let mut builder = builders::dotnet::DotnetBindingBuilder::new(
settings.clone(),
args.target_framework,
platforms.dotnet,
&args.extra_files,
);
builder.run(options);
}
if args.build_java {
let mut builder =
builders::java::JavaBindingBuilder::new(settings, platforms.java, &args.extra_files);
builder.run(options);
}
}
struct LanguagePlatforms {
cpp: PlatformLocations,
dotnet: PlatformLocations,
java: PlatformLocations,
}
impl LanguagePlatforms {
fn same(locations: PlatformLocations) -> Self {
Self {
cpp: locations.clone(),
dotnet: locations.clone(),
java: locations,
}
}
}
fn get_single_platform(args: &Args) -> (RunOptions, LanguagePlatforms) {
let artifact_dir = match &args.artifact_dir {
Some(x) => {
tracing::info!("Artifact dir is {}", x.display());
x.clone()
}
None => {
let x: PathBuf = "./target/release".into();
tracing::info!("No artifact dir specified, assuming: {}", x.display());
x
}
};
let platform = match &args.target_triple {
None => {
let platform = Platform::guess_current().expect("Could not determine current platform");
tracing::info!(
"No target platform specified assuming target is the host platform: {}",
platform
);
platform
}
Some(tt) => match Platform::find(tt) {
None => panic!("Unable to determine Platform from target triple: {tt}"),
Some(x) => x,
},
};
let mut platforms = PlatformLocations::new();
platforms.add(platform.clone(), artifact_dir);
let options = RunOptions {
test: !args.no_tests,
package: false,
docs: args.generate_doxygen,
};
(options, LanguagePlatforms::same(platforms))
}
fn get_packaging_platforms(
dir: &PathBuf,
options: PackageOptions,
) -> (RunOptions, LanguagePlatforms) {
let mut platforms = PlatformLocations::new();
for entry in fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();
if path.is_dir() {
if let Some(p) = Platform::find(&entry.file_name().to_string_lossy()) {
platforms.add(p.clone(), entry.path());
}
}
}
assert!(!platforms.is_empty(), "No platforms found!");
for p in platforms.iter() {
tracing::info!("Platform {} in {}", p.platform, p.location.display());
}
let cpp = {
let mut cpp = PlatformLocations::new();
for p in platforms.iter() {
if options.package_cpp(&p.platform) {
cpp.locations.push(p.clone());
} else {
tracing::warn!("Ignoring available C/C++ package {}", p.platform)
}
}
cpp
};
let dotnet = {
let mut dotnet = PlatformLocations::new();
for p in platforms.iter() {
if options.package_dotnet(&p.platform) {
dotnet.locations.push(p.clone());
} else {
tracing::warn!("Ignoring available .NET package {}", p.platform)
}
}
dotnet
};
let java = {
let mut java = PlatformLocations::new();
for p in platforms.iter() {
if options.package_java(&p.platform) {
java.locations.push(p.clone());
} else {
tracing::warn!("Ignoring available Java package {}", p.platform)
}
}
java
};
let options = RunOptions {
test: false,
package: true,
docs: false,
};
(options, LanguagePlatforms { cpp, dotnet, java })
}
fn get_platforms(args: &Args) -> (RunOptions, LanguagePlatforms) {
if let Some(dir) = &args.package_dir {
let config_path = args
.package_options
.as_ref()
.expect("You must specify the options file when packaging");
let options: PackageOptions = {
let file = File::open(config_path).expect("Error opening package options file");
serde_json::from_reader(file).expect("Error reading package options JSON")
};
get_packaging_platforms(dir, options)
} else {
get_single_platform(args)
}
}
#[derive(Clone)]
pub struct BindingBuilderSettings {
pub ffi_target_name: &'static str,
pub jni_target_name: &'static str,
pub ffi_name: &'static str,
pub ffi_path: PathBuf,
pub java_group_id: &'static str,
pub destination_path: PathBuf,
pub library: Rc<Library>,
}
#[derive(Copy, Clone, Debug)]
struct RunOptions {
pub(crate) test: bool,
pub(crate) package: bool,
pub(crate) docs: bool,
}
impl RunOptions {
pub(crate) fn new() -> Self {
Self {
test: false,
package: false,
docs: false,
}
}
}
impl Default for RunOptions {
fn default() -> Self {
Self::new()
}
}
trait BindingBuilder: Sized {
fn name() -> &'static str;
fn generate(&mut self, is_packaging: bool, generate_docs: bool);
fn build(&mut self);
fn test(&mut self);
fn package(&mut self);
fn run(&mut self, options: RunOptions) {
let span = tracing::info_span!("generate()", lang = Self::name());
span.in_scope(|| {
tracing::info!("begin");
self.generate(options.package, options.docs);
tracing::info!("end");
});
if options.package {
let span = tracing::info_span!("package()", lang = Self::name());
span.in_scope(|| {
tracing::info!("begin");
self.package();
tracing::info!("end");
});
} else if options.test {
let span = tracing::info_span!("build()", lang = Self::name());
span.in_scope(|| {
tracing::info!("begin");
self.build();
tracing::info!("end");
});
let span = tracing::info_span!("test()", lang = Self::name());
span.in_scope(|| {
tracing::info!("begin");
self.test();
tracing::info!("end");
});
}
}
}