1pub(crate) mod args;
2pub(crate) mod builders;
3
4use std::fs;
5use std::fs::File;
6use std::path::PathBuf;
7use std::rc::Rc;
8
9use crate::backend::*;
10use crate::model::Library;
11
12use crate::cli::args::{Args, PackageOptions};
13
14pub fn run(settings: BindingBuilderSettings) {
16 let args = Args::get();
17
18 let (options, platforms) = {
19 let span = tracing::info_span!("configure()");
20 span.in_scope(|| get_platforms(&args))
21 };
22
23 if args.build_c {
24 let mut builder =
25 builders::c::CBindingBuilder::new(settings.clone(), platforms.cpp, &args.extra_files);
26 builder.run(options);
27 }
28 if args.build_dotnet {
29 let mut builder = builders::dotnet::DotnetBindingBuilder::new(
30 settings.clone(),
31 args.target_framework,
32 platforms.dotnet,
33 &args.extra_files,
34 );
35 builder.run(options);
36 }
37 if args.build_java {
38 let mut builder =
39 builders::java::JavaBindingBuilder::new(settings, platforms.java, &args.extra_files);
40 builder.run(options);
41 }
42}
43
44struct LanguagePlatforms {
45 cpp: PlatformLocations,
46 dotnet: PlatformLocations,
47 java: PlatformLocations,
48}
49
50impl LanguagePlatforms {
51 fn same(locations: PlatformLocations) -> Self {
52 Self {
53 cpp: locations.clone(),
54 dotnet: locations.clone(),
55 java: locations,
56 }
57 }
58}
59
60fn get_single_platform(args: &Args) -> (RunOptions, LanguagePlatforms) {
61 let artifact_dir = match &args.artifact_dir {
62 Some(x) => {
63 tracing::info!("Artifact dir is {}", x.display());
64 x.clone()
65 }
66 None => {
67 let x: PathBuf = "./target/release".into();
68 tracing::info!("No artifact dir specified, assuming: {}", x.display());
69 x
70 }
71 };
72
73 let platform = match &args.target_triple {
74 None => {
75 let platform = Platform::guess_current().expect("Could not determine current platform");
76 tracing::info!(
77 "No target platform specified assuming target is the host platform: {}",
78 platform
79 );
80 platform
81 }
82 Some(tt) => match Platform::find(tt) {
83 None => panic!("Unable to determine Platform from target triple: {tt}"),
84 Some(x) => x,
85 },
86 };
87
88 let mut platforms = PlatformLocations::new();
89 platforms.add(platform.clone(), artifact_dir);
90
91 let options = RunOptions {
92 test: !args.no_tests,
93 package: false,
94 docs: args.generate_doxygen,
95 };
96 (options, LanguagePlatforms::same(platforms))
97}
98
99fn get_packaging_platforms(
100 dir: &PathBuf,
101 options: PackageOptions,
102) -> (RunOptions, LanguagePlatforms) {
103 let mut platforms = PlatformLocations::new();
104 for entry in fs::read_dir(dir).unwrap() {
105 let entry = entry.unwrap();
106 let path = entry.path();
107 if path.is_dir() {
108 if let Some(p) = Platform::find(&entry.file_name().to_string_lossy()) {
109 platforms.add(p.clone(), entry.path());
110 }
111 }
112 }
113
114 assert!(!platforms.is_empty(), "No platforms found!");
115
116 for p in platforms.iter() {
117 tracing::info!("Platform {} in {}", p.platform, p.location.display());
118 }
119
120 let cpp = {
121 let mut cpp = PlatformLocations::new();
122 for p in platforms.iter() {
123 if options.package_cpp(&p.platform) {
124 cpp.locations.push(p.clone());
125 } else {
126 tracing::warn!("Ignoring available C/C++ package {}", p.platform)
127 }
128 }
129 cpp
130 };
131
132 let dotnet = {
133 let mut dotnet = PlatformLocations::new();
134 for p in platforms.iter() {
135 if options.package_dotnet(&p.platform) {
136 dotnet.locations.push(p.clone());
137 } else {
138 tracing::warn!("Ignoring available .NET package {}", p.platform)
139 }
140 }
141 dotnet
142 };
143
144 let java = {
145 let mut java = PlatformLocations::new();
146 for p in platforms.iter() {
147 if options.package_java(&p.platform) {
148 java.locations.push(p.clone());
149 } else {
150 tracing::warn!("Ignoring available Java package {}", p.platform)
151 }
152 }
153 java
154 };
155
156 let options = RunOptions {
157 test: false,
158 package: true,
159 docs: false,
160 };
161
162 (options, LanguagePlatforms { cpp, dotnet, java })
163}
164
165fn get_platforms(args: &Args) -> (RunOptions, LanguagePlatforms) {
166 if let Some(dir) = &args.package_dir {
167 let config_path = args
168 .package_options
169 .as_ref()
170 .expect("You must specify the options file when packaging");
171 let options: PackageOptions = {
172 let file = File::open(config_path).expect("Error opening package options file");
173 serde_json::from_reader(file).expect("Error reading package options JSON")
174 };
175
176 get_packaging_platforms(dir, options)
177 } else {
178 get_single_platform(args)
179 }
180}
181
182#[derive(Clone)]
184pub struct BindingBuilderSettings {
185 pub ffi_target_name: &'static str,
187 pub jni_target_name: &'static str,
189 pub ffi_name: &'static str,
191 pub ffi_path: PathBuf,
193 pub java_group_id: &'static str,
195 pub destination_path: PathBuf,
197 pub library: Rc<Library>,
199}
200
201#[derive(Copy, Clone, Debug)]
203struct RunOptions {
204 pub(crate) test: bool,
206 pub(crate) package: bool,
208 pub(crate) docs: bool,
210}
211
212impl RunOptions {
213 pub(crate) fn new() -> Self {
214 Self {
215 test: false,
216 package: false,
217 docs: false,
218 }
219 }
220}
221
222impl Default for RunOptions {
223 fn default() -> Self {
224 Self::new()
225 }
226}
227
228trait BindingBuilder: Sized {
229 fn name() -> &'static str;
230 fn generate(&mut self, is_packaging: bool, generate_docs: bool);
231 fn build(&mut self);
232 fn test(&mut self);
233 fn package(&mut self);
234
235 fn run(&mut self, options: RunOptions) {
236 let span = tracing::info_span!("generate()", lang = Self::name());
237 span.in_scope(|| {
238 tracing::info!("begin");
239 self.generate(options.package, options.docs);
240 tracing::info!("end");
241 });
242
243 if options.package {
244 let span = tracing::info_span!("package()", lang = Self::name());
245 span.in_scope(|| {
246 tracing::info!("begin");
247 self.package();
248 tracing::info!("end");
249 });
250 } else if options.test {
251 let span = tracing::info_span!("build()", lang = Self::name());
252 span.in_scope(|| {
253 tracing::info!("begin");
254 self.build();
255 tracing::info!("end");
256 });
257 let span = tracing::info_span!("test()", lang = Self::name());
258 span.in_scope(|| {
259 tracing::info!("begin");
260 self.test();
261 tracing::info!("end");
262 });
263 }
264 }
265}