oo_bindgen/cli/
mod.rs

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
14/// Run the binding generator
15pub 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/// Settings that control binding generation
183#[derive(Clone)]
184pub struct BindingBuilderSettings {
185    /// FFI target name (as specified in with `cargo build -p <...>`)
186    pub ffi_target_name: &'static str,
187    /// JNI target name (as specified in with `cargo build -p <...>`)
188    pub jni_target_name: &'static str,
189    /// Compiled FFI name (usually the same as `ffi_target_name`, but with hyphens replaced by underscores)
190    pub ffi_name: &'static str,
191    /// Path to the FFI target
192    pub ffi_path: PathBuf,
193    /// Name of the Java group (e.g. `io.stepfunc`)
194    pub java_group_id: &'static str,
195    /// Destination path
196    pub destination_path: PathBuf,
197    /// Library to build
198    pub library: Rc<Library>,
199}
200
201/// options for an invocation of a binding builder
202#[derive(Copy, Clone, Debug)]
203struct RunOptions {
204    /// run the tests
205    pub(crate) test: bool,
206    /// package the library if this is a separate step from generation
207    pub(crate) package: bool,
208    /// generate the docs if this is a separate step
209    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}