1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use itertools::Itertools;

use super::Put;
use crate::utils::cmake;

#[derive(Debug, Clone, Default)]
pub struct BundleBuilder {
    puts: Vec<Put>,
}

impl BundleBuilder {
    pub fn new(puts: Vec<Put>) -> Self {
        Self { puts }
    }

    pub fn build(self, out_dir: impl AsRef<std::path::Path>) -> Bundle {
        let out_dir = out_dir.as_ref();

        let bundle_bindings = format!(
            r#"
            #[allow(unused_macros)]
            #[macro_export]
            macro_rules! for_puts {{
                ( $($tail:tt)* ) => {{
                    {puts_macros}
                }};
            }}

            pub use for_puts;

            fn register() -> Vec<GlobalFactory> {{
                vec![None, {registration}].into_iter().flatten().collect()
            }}

            {puts_modules}
            "#,
            registration = self
                .puts
                .iter()
                .map(|put| format!("{}::register()", put.id()))
                .join(", "),
            puts_modules = self
                .puts
                .iter()
                .map(|put| put.rust_registration())
                .join("\n\n"),
            puts_macros = self.puts.iter().map(|put| {
               format!(r#"puffin_macros::replace!(__PUTSTR__ => "{name}" in {{ puffin_macros::replace!(__PUT__ => {id} in {{ $($tail)* }}); }});"#, name = put.name(), id = put.id())
            }).join("\n")
        );

        std::fs::create_dir_all(out_dir).unwrap();
        std::fs::write(out_dir.join(Bundle::bindings_file()), bundle_bindings)
            .expect("failed to write Rust PUT bindings");

        let mut cmake_conf = cmake::command("bundle", out_dir);

        cmake_conf.cfg_args.push(format!(
            "-D=PUTS={}",
            self.puts
                .iter()
                .flat_map(|put| put.objects())
                .map(|obj| obj.display().to_string())
                .collect::<Vec<_>>()
                .join(",")
        ));

        cmake_conf.build().unwrap();

        Bundle::new(self.puts, out_dir.to_path_buf())
    }
}

pub struct Bundle {
    puts: Vec<Put>,
    dir: std::path::PathBuf,
}

impl Bundle {
    pub fn print_cargo_metadata(&self) {
        for put in self.puts.iter() {
            put.print_cargo_metadata();
        }

        println!(
            "cargo:rustc-env=RUST_PUTS_BUNDLE_FILE={}",
            self.dir.join(Self::bindings_file()).display()
        );

        println!("cargo:rustc-link-search=native={}", self.dir.display());
        println!("cargo:rustc-link-lib=static=puts-bundle");
    }

    pub(in crate::harness) fn new(puts: Vec<Put>, dir: impl Into<std::path::PathBuf>) -> Self {
        Self {
            puts,
            dir: dir.into(),
        }
    }

    pub(self) fn bindings_file() -> std::path::PathBuf {
        std::path::PathBuf::from("put_bindings.rs")
    }
}