rusty_bind_build/
lib.rs

1//
2// Wildland Project
3//
4// Copyright © 2022 Golem Foundation,
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License version 3 as published by
8// the Free Software Foundation.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <https://www.gnu.org/licenses/>.
17
18use std::io::Write;
19
20use anyhow::Context as _;
21use cfg_expr::targets::get_builtin_target_by_triple;
22use rusty_bind_parser::binding_module::CargoFeature;
23use rusty_bind_parser::BindingModule;
24use syn::{File, Item};
25
26/// The function parses a Rust source file and searches for a module decorated
27/// with `#[binding_wrapper]` attribute. Once the module is provided it parses
28/// it and generates gluecode for: SWIG interface, C++ and Swift.
29///
30pub fn parse_ffi_module(input_rust_file_path: &str, output_dir: &str) -> anyhow::Result<()> {
31    let file = std::fs::read_to_string(input_rust_file_path)
32        .with_context(|| format!("Could not read a file `{input_rust_file_path}`"))?;
33
34    if !std::path::Path::new(output_dir).exists() {
35        std::fs::create_dir(output_dir)
36            .with_context(|| format!("Could not create a directory `{output_dir}`"))?;
37    }
38
39    let context = build_context().context("Failed to build context")?;
40
41    // Don't return an error in case of any problems with parsing -
42    // user will be informed about them in the further process of the compilation.
43    //
44    if let Ok(file) = syn::parse_str(&file) as Result<File, _> {
45        for item in file.items {
46            if let Item::Mod(module) = item {
47                let parsed = BindingModule::translate_module(module, &context)?;
48                let file = syn::parse2(parsed.get_tokens())?;
49                let rust_code = prettyplease::unparse(&file);
50                let generated_code = parsed.generate_binding_files();
51
52                let mut output_interface = std::fs::File::create(format!(
53                    "{output_dir}/interface.rs"
54                ))
55                .with_context(|| format!("Could not create a file `{output_dir}/interface.rs`"))?;
56                output_interface
57                    .write_all(rust_code.as_bytes())
58                    .with_context(|| format!("Could not write file `{output_dir}/interface.rs`"))?;
59
60                let mut output_interface = std::fs::File::create(format!("{output_dir}/ffi_cxx.h"))
61                    .with_context(|| format!("Could not create a file `{output_dir}/ffi_cxx.h`"))?;
62                output_interface
63                    .write_all(generated_code.cpp_header.as_bytes())
64                    .with_context(|| format!("Could not write file `{output_dir}/ffi_cxx.h`"))?;
65
66                let mut output_interface =
67                    std::fs::File::create(format!("{output_dir}/ffi_swift.swift")).with_context(
68                        || format!("Could not create a file `{output_dir}/ffi_swift.swift`"),
69                    )?;
70                output_interface
71                    .write_all(generated_code.swift_header.as_bytes())
72                    .with_context(|| {
73                        format!("Could not write file `{output_dir}/ffi_swift.swift`")
74                    })?;
75
76                let mut output_interface = std::fs::File::create(format!(
77                    "{output_dir}/ffi_swift.h"
78                ))
79                .with_context(|| format!("Could not create a file `{output_dir}/ffi_swift.h`"))?;
80                output_interface
81                    .write_all(generated_code.cpp_externs.as_bytes())
82                    .with_context(|| format!("Could not write file `{output_dir}/ffi_swift.h`"))?;
83
84                let mut output_interface = std::fs::File::create(format!(
85                    "{output_dir}/ffi_swig.i"
86                ))
87                .with_context(|| format!("Could not create a file `{output_dir}/ffi_swig.i`"))?;
88                output_interface
89                    .write_all(generated_code.swig_interface.as_bytes())
90                    .with_context(|| {
91                        format!("Could not write to file `{output_dir}/ffi_swig.i`")
92                    })?;
93            }
94        }
95    }
96    Ok(())
97}
98
99fn build_context() -> anyhow::Result<rusty_bind_parser::BuildContext> {
100    let target = std::env::var("TARGET").context("TARGET env variable is not present")?;
101    let current_target_info =
102        get_builtin_target_by_triple(&target).context("Fail to parse TARGET env")?;
103
104    let features = std::env::vars()
105        .map(|kv| kv.0)
106        .filter(|key| key.starts_with("CARGO_FEATURE_"))
107        .map(|key| CargoFeature::new(&key["CARGO_FEATURE_".len()..]))
108        .collect();
109
110    let target_features = std::env::var("CARGO_CFG_TARGET_FEATURE")
111        .unwrap_or_default()
112        .split(',')
113        .map(Into::into)
114        .collect();
115
116    Ok(rusty_bind_parser::BuildContext::new(
117        current_target_info,
118        features,
119        target_features,
120    ))
121}