rsbind_core/
lib.rs

1#![recursion_limit = "128"]
2extern crate cbindgen;
3extern crate core;
4#[macro_use]
5extern crate error_chain;
6extern crate fs_extra;
7extern crate heck;
8extern crate ndk_build;
9extern crate ndk_tool;
10extern crate proc_macro2;
11#[macro_use]
12extern crate quote;
13#[macro_use]
14extern crate rstgen;
15extern crate serde;
16#[macro_use]
17extern crate serde_derive;
18extern crate serde_json;
19extern crate syn;
20extern crate toml;
21extern crate zip;
22
23use std::fs;
24use std::path::PathBuf;
25
26use crate::android::config::Android;
27use crate::android::process::AndroidProcess;
28use crate::ast::AstResult;
29use crate::base::process::*;
30use crate::errors::*;
31use crate::ios::config::Ios;
32use crate::ios::process::IosProcess;
33use crate::jar::config::Jar;
34use crate::jar::process::JarProcess;
35use crate::mac::config::Mac;
36use crate::mac::process::MacProcess;
37
38mod android;
39mod ast;
40mod base;
41mod bridge;
42mod cargo;
43mod config;
44mod errors;
45mod ios;
46mod jar;
47mod java;
48mod mac;
49mod swift;
50mod test;
51mod unzip;
52#[macro_use]
53mod common;
54
55const GEN_DIR_NAME: &str = "_gen";
56const HEADER_NAME: &str = "header";
57const AST_DIR: &str = "ast";
58const IOS_PROJ: &str = "ios_artifact";
59const IOS_BRIDGE_PROJ: &str = "ios_bridge";
60const MAC_PROJ: &str = "mac_artifact";
61const MAC_BRIDGE_PROJ: &str = "mac_bridge";
62const ANDROID_BRIDGE_PROJ: &str = "android_bridge";
63const ANDROID_PROJ: &str = "android_artifact";
64const JAR_BRIDGE_PROJ: &str = "jar_bridge";
65const JAR_PROJ: &str = "jar_artifact";
66
67pub struct Bind {
68    prj_path: PathBuf,
69    ios_artifact_path: PathBuf,
70    ios_bridge_path: PathBuf,
71    mac_artifact_path: PathBuf,
72    mac_bridge_path: PathBuf,
73    android_bridge_path: PathBuf,
74    android_artifact_path: PathBuf,
75    jar_bridge_path: PathBuf,
76    jar_artifact_path: PathBuf,
77    header_path: PathBuf,
78    ast_path: PathBuf,
79    target: Target,
80    action: Action,
81}
82
83pub enum Target {
84    Android,
85    Ios,
86    Mac,
87    Jar,
88    All,
89}
90
91pub enum Action {
92    /// Parse src/contract and src/imp, generate simplified ast json file to _gen/ast.
93    GenAst,
94    /// Generate rust bridge code to _gen/ios_bridge or _gen/android_bridge, for iOS it's c ffi functions, for android it's jni ffi functions.
95    GenBridge,
96    /// Generate code in artifact(iOS framework or android aar)
97    GenArtifactCode,
98    /// Generate c header file
99    GenCHeader,
100    /// Build and generate artifact.
101    BuildArtifact,
102    /// Do all the process and generate artifacts.
103    All,
104}
105
106impl Bind {
107    ///
108    /// crate the object for binding.
109    /// * prject_path: the rust project we need to bind
110    /// * target: which target we want to generate, android or iOS
111    ///
112    pub fn from(prj_path: String, target: Target, action: Action) -> Bind {
113        let root = PathBuf::from(&prj_path);
114
115        // ./_gen/ast
116        let ast_path = root.join(GEN_DIR_NAME).join(AST_DIR);
117
118        // ./_gen/header/
119        let header_path = root.join(GEN_DIR_NAME).join(HEADER_NAME);
120
121        // ./_gen/ios_artifact/
122        let ios_artifact_path = root.join(GEN_DIR_NAME).join(IOS_PROJ);
123
124        // ./_gen/ios_bridge
125        let ios_bridge_path = root.join(GEN_DIR_NAME).join(IOS_BRIDGE_PROJ);
126
127        // ./_gen/mac_artifact/
128        let mac_artifact_path = root.join(GEN_DIR_NAME).join(MAC_PROJ);
129
130        // ./_gen/mac_bridge
131        let mac_bridge_path = root.join(GEN_DIR_NAME).join(MAC_BRIDGE_PROJ);
132
133        // ./_gen/android_bridge
134        let android_bridge_path = root.join(GEN_DIR_NAME).join(ANDROID_BRIDGE_PROJ);
135
136        let android_artifact_path = root.join(GEN_DIR_NAME).join(ANDROID_PROJ);
137
138        // ./_gen/jar_bridge
139        let jar_bridge_path = root.join(GEN_DIR_NAME).join(JAR_BRIDGE_PROJ);
140
141        let jar_artifact_path = root.join(GEN_DIR_NAME).join(JAR_PROJ);
142
143        Bind {
144            prj_path: root,
145            ios_artifact_path,
146            ios_bridge_path,
147            mac_artifact_path,
148            mac_bridge_path,
149            android_bridge_path,
150            android_artifact_path,
151            jar_bridge_path,
152            jar_artifact_path,
153            header_path,
154            ast_path,
155            target,
156            action,
157        }
158    }
159
160    ///
161    /// generate the ios framework and android aar as per the target config
162    ///
163    pub fn gen_all(&self) -> Result<()> {
164        let config = config::parse(&self.prj_path);
165        println!("rsbind config in {:?} is {:?}", &self.prj_path, config);
166
167        let crate_name = self.parse_crate_name()?;
168
169        if let Action::GenAst = self.action {
170            self.parse_ast(crate_name)?;
171            return Ok(());
172        }
173
174        let ast = &self.get_ast_if_need(crate_name.clone())?;
175        match self.target {
176            Target::Ios => {
177                self.gen_for_ios(&crate_name, ast, config)?;
178            }
179            Target::Android => {
180                self.gen_for_android(&crate_name, ast, config)?;
181            }
182            Target::Mac => {
183                self.gen_for_mac(&crate_name, ast, config)?;
184            }
185            Target::Jar => {
186                self.gen_for_jar(&crate_name, ast, config)?;
187            }
188            Target::All => {
189                self.gen_for_ios(&crate_name, ast, config.clone())?;
190                self.gen_for_android(&crate_name, ast, config.clone())?;
191                self.gen_for_mac(&crate_name, ast, config.clone())?;
192                self.gen_for_jar(&crate_name, ast, config)?;
193            }
194        };
195        Ok(())
196    }
197
198    fn get_ast_if_need(&self, crate_name: String) -> Result<AstResult> {
199        match self.action {
200            Action::GenBridge | Action::GenArtifactCode | Action::All => self.parse_ast(crate_name),
201            _ => {
202                use std::collections::HashMap;
203                let ast_result = AstResult {
204                    traits: HashMap::new(),
205                    structs: HashMap::new(),
206                    imps: vec![],
207                };
208                Ok(ast_result)
209            }
210        }
211    }
212
213    fn parse_ast(&self, crate_name: String) -> Result<AstResult> {
214        let prj_path = PathBuf::from(&self.prj_path);
215        if self.ast_path.exists() {
216            fs::remove_dir_all(&self.ast_path)?;
217        }
218        fs::create_dir_all(&self.ast_path)?;
219        ast::AstHandler::new(crate_name)
220            .parse(&prj_path)?
221            .flush(&self.ast_path)
222    }
223
224    ///
225    /// generate the jar framework
226    fn gen_for_jar(
227        &self,
228        crate_name: &str,
229        ast_result: &AstResult,
230        config: Option<config::Config>,
231    ) -> Result<()> {
232        let jar = match config {
233            Some(ref config) => config.jar.clone(),
234            None => Some(Jar::default()),
235        };
236
237        let jar_process = JarProcess::new(
238            &self.prj_path,
239            &self.jar_artifact_path,
240            &self.jar_bridge_path,
241            crate_name,
242            ast_result,
243            jar,
244        );
245
246        match self.action {
247            Action::GenAst => (),
248            Action::GenBridge => jar_process.gen_bridge_src()?,
249            Action::GenArtifactCode => jar_process.gen_artifact_code()?,
250            Action::GenCHeader => {}
251            Action::BuildArtifact => {
252                jar_process.build_bridge_prj()?;
253                jar_process.copy_bridge_outputs()?;
254                jar_process.build_artifact_prj()?;
255            }
256            Action::All => {
257                jar_process.gen_bridge_src()?;
258                jar_process.gen_artifact_code()?;
259                jar_process.build_bridge_prj()?;
260                jar_process.copy_bridge_outputs()?;
261                jar_process.build_artifact_prj()?;
262            }
263        }
264
265        Ok(())
266    }
267
268    ///
269    /// generate the mac framework
270    fn gen_for_mac(
271        &self,
272        crate_name: &str,
273        ast_result: &AstResult,
274        config: Option<config::Config>,
275    ) -> Result<()> {
276        let mac = match config {
277            Some(ref config) => config.mac.clone(),
278            None => Some(Mac::default()),
279        };
280
281        let mac_process = MacProcess::new(
282            &self.prj_path,
283            &self.mac_artifact_path,
284            &self.mac_bridge_path,
285            &self.header_path,
286            crate_name,
287            ast_result,
288            mac,
289        );
290
291        match self.action {
292            Action::GenAst => (),
293            Action::GenBridge => mac_process.gen_bridge_src()?,
294            Action::GenArtifactCode => mac_process.gen_artifact_code()?,
295            Action::GenCHeader => mac_process.gen_c_header()?,
296            Action::BuildArtifact => {
297                mac_process.build_bridge_prj()?;
298                mac_process.copy_bridge_outputs()?;
299                // we don't generate artifact now. TODO generate cocoapods lib
300                // ios_process.build_artifact_prj()?;
301            }
302            Action::All => {
303                mac_process.gen_bridge_src()?;
304                mac_process.gen_artifact_code()?;
305                mac_process.build_bridge_prj()?;
306                mac_process.copy_bridge_outputs()?;
307                // we don't generate artifact now. TODO generate cocoapods lib
308                // ios_process.build_artifact_prj()?;
309            }
310        }
311
312        Ok(())
313    }
314
315    ///
316    /// generate the ios framework
317    fn gen_for_ios(
318        &self,
319        crate_name: &str,
320        ast_result: &AstResult,
321        config: Option<config::Config>,
322    ) -> Result<()> {
323        let ios = match config {
324            Some(ref config) => config.ios.clone(),
325            None => Some(Ios::default()),
326        };
327
328        let ios_process = IosProcess::new(
329            &self.prj_path,
330            &self.ios_artifact_path,
331            &self.ios_bridge_path,
332            &self.header_path,
333            crate_name,
334            ast_result,
335            ios,
336        );
337
338        match self.action {
339            Action::GenAst => (),
340            Action::GenBridge => ios_process.gen_bridge_src()?,
341            Action::GenArtifactCode => ios_process.gen_artifact_code()?,
342            Action::GenCHeader => ios_process.gen_c_header()?,
343            Action::BuildArtifact => {
344                ios_process.build_bridge_prj()?;
345                ios_process.copy_bridge_outputs()?;
346                // we don't generate artifact now. TODO generate cocoapods lib
347                // ios_process.build_artifact_prj()?;
348            }
349            Action::All => {
350                ios_process.gen_bridge_src()?;
351                ios_process.gen_artifact_code()?;
352                ios_process.build_bridge_prj()?;
353                ios_process.copy_bridge_outputs()?;
354                // we don't generate artifact now. TODO generate cocoapods lib
355                // ios_process.build_artifact_prj()?;
356            }
357        }
358
359        Ok(())
360    }
361
362    ///
363    /// generate the android aar
364    ///
365    fn gen_for_android(
366        &self,
367        crate_name: &str,
368        ast_result: &AstResult,
369        config: Option<config::Config>,
370    ) -> Result<()> {
371        let android = match config {
372            Some(ref config) => config.android.clone(),
373            None => Some(Android::default()),
374        };
375
376        let android_process = AndroidProcess::new(
377            &self.prj_path,
378            &self.android_artifact_path,
379            &self.android_bridge_path,
380            crate_name,
381            ast_result,
382            android,
383        );
384
385        match self.action {
386            Action::GenAst => (),
387            Action::GenBridge => android_process.gen_bridge_src()?,
388            Action::GenArtifactCode => android_process.gen_artifact_code()?,
389            Action::GenCHeader => (),
390            Action::BuildArtifact => {
391                android_process.build_bridge_prj()?;
392                android_process.copy_bridge_outputs()?;
393                android_process.build_artifact_prj()?;
394            }
395            Action::All => {
396                android_process.gen_bridge_src()?;
397                android_process.gen_artifact_code()?;
398                android_process.build_bridge_prj()?;
399                android_process.copy_bridge_outputs()?;
400                android_process.build_artifact_prj()?;
401            }
402        };
403
404        Ok(())
405    }
406
407    ///
408    /// parse the crate name of origin project from Cargo.toml
409    ///
410    fn parse_crate_name(&self) -> Result<String> {
411        let toml_path = PathBuf::from(&self.prj_path).join("Cargo.toml");
412        let manifest = cargo::manifest(toml_path.as_path())?;
413        println!("parse project name = {}", manifest.package.name);
414        Ok(manifest.package.name)
415    }
416}