1use std::{
2 env::current_dir,
3 fs::{copy, create_dir, create_dir_all, read_dir, write},
4 path::PathBuf,
5 process::{Command, Stdio},
6 sync::Mutex,
7};
8
9use proc_macro::TokenStream;
10
11extern crate proc_macro;
12
13const CARGO_TOML_CONTENTS: &str = r#"[package]
14name = "transarch-tmp-pkg"
15version = "0.1.0"
16edition = "2021"
17
18[lib]
19crate-type = ["cdylib", "rlib"]
20
21[workspace]
22members = []
23"#;
24
25static mut COMP_BLOB_ID: Mutex<usize> = Mutex::new(0);
26
27fn target_dir() -> PathBuf {
28 current_dir()
29 .unwrap()
30 .ancestors()
31 .find(|x| x.join("Cargo.toml").is_file() && x.join("target").is_dir())
32 .map(|x| x.join("target"))
33 .expect("no target dir found. what did you do?")
34}
35
36fn build(tokens: TokenStream, target: Option<String>) -> (PathBuf, String) {
37 let dir = target_dir().join("transarch/transarch-tmp-crate");
38
39 if !dir.exists() {
40 create_dir_all(dir.clone()).expect("setup failed: failed to create <TRANSARCH-ROOT>");
41 create_dir(dir.join("src")).expect("setup failed: failed to create <TRANSARCH-ROOT>/src");
42 write(dir.join("Cargo.toml"), CARGO_TOML_CONTENTS)
43 .expect("setup failed: failed to create <TRANSARCH-ROOT>/Cargo.toml");
44 }
45
46 let mut iter = tokens.into_iter();
47
48 let target = target.unwrap_or_else(|| {
49 iter.next()
50 .and_then(|x| match x {
51 proc_macro::TokenTree::Literal(x) => {
52 let x = x.to_string();
53 if !x.starts_with('"') || !x.is_ascii() {
54 panic!("target must be a string");
55 }
56
57 Some(x[1..x.len() - 1].to_string())
58 }
59 _ => None,
60 })
61 .expect("no target provided")
62 });
63
64 write(
65 dir.join("src/lib.rs"),
66 TokenStream::from_iter(iter).to_string(),
67 )
68 .expect("setup failed: failed to create <TRANSARCH-ROOT>/src/lib.rs");
69
70 let mut cmd = Command::new("cargo")
71 .arg("build")
72 .arg(format!("--target={target}"))
73 .arg("--color=always")
74 .stdout(Stdio::inherit())
75 .stderr(Stdio::inherit())
76 .current_dir(&dir)
77 .spawn()
78 .expect("failed to run cargo");
79
80 if !cmd.wait().expect("how").success() {
81 panic!("failed to execute cargo");
82 }
83
84 (dir, target)
85}
86
87#[proc_macro]
88pub fn cross(tokens: TokenStream) -> TokenStream {
98 let mut id = unsafe { COMP_BLOB_ID.lock().unwrap() };
99
100 let (dir, target) = build(tokens, None);
101
102 let out_dir = dir.join(format!("target/{target}/debug"));
103
104 let mut buf = String::new();
105
106 fn make(buf: &mut String, path: PathBuf, out_dir: PathBuf, blob_id: &mut usize) {
107 if path.is_dir() {
108 buf.push_str("Into::<::transarch::Dir>::into({");
109 buf.push_str("let mut map=::std::collections::HashMap::new();");
110 for x in read_dir(path).unwrap() {
111 let x = x.unwrap();
112 if x.path().is_dir() {
113 buf.push_str(&format!(
114 r#"map.insert("{}",::transarch::Entry::Dir("#,
115 x.file_name().into_string().unwrap()
116 ));
117 } else {
118 buf.push_str(&format!(
119 r#"map.insert("{}",::transarch::Entry::File("#,
120 x.file_name().into_string().unwrap()
121 ));
122 }
123 make(buf, x.path(), out_dir.clone(), blob_id);
124 buf.push_str("));");
125 }
126 buf.push_str("map})");
127 return;
128 }
129
130 let file = out_dir.join(format!("../blob{}", *blob_id));
131 copy(path, &file).expect("your drive is full. do `cargo clean`");
132 *blob_id += 1;
133 buf.push_str(&format!("include_bytes!({:?})", file.display()));
134 }
135
136 make(&mut buf, out_dir.clone(), out_dir, &mut id);
137
138 buf.parse().unwrap()
139}
140
141#[proc_macro]
142pub fn wasm(tokens: TokenStream) -> TokenStream {
152 let mut id = unsafe { COMP_BLOB_ID.lock().unwrap() };
153
154 let (dir, target) = build(tokens, Some("wasm32-unknown-unknown".to_string()));
155
156 let out_dir = dir.join(format!("target/{target}/debug"));
157
158 let file = out_dir.join(format!("../blob{}", *id));
159 copy(out_dir.join("transarch_tmp_pkg.wasm"), &file).expect("copy failed");
160
161 *id += 1;
162
163 format!("include_bytes!({:?})", file.display())
164 .parse()
165 .unwrap()
166}