105 lines
6 KiB
Rust
105 lines
6 KiB
Rust
use relax_lex::tokens::{Delimiter, Span, TokenStream, TokenTree};
|
|
use std::path::PathBuf;
|
|
|
|
#[path = "../build_sources.rs"]
|
|
mod build_sources;
|
|
#[path = "src/native_cfg.rs"]
|
|
mod native_cfg;
|
|
|
|
// Package, Makepad path, and (for proc-macro wrappers) the generated module.
|
|
const PACKAGES: &[(&str, &str, Option<&str>)] = &[
|
|
("makepad_micro_proc_macro", "libs/micro_proc_macro", None),
|
|
("makepad_live_id", "libs/live_id", None),
|
|
("makepad_live_id_macros", "libs/live_id/id_macros", Some("id")),
|
|
("makepad_live_id_macros_impl", "libs/live_id/id_macros_impl", None),
|
|
("makepad_micro_serde_derive", "libs/micro_serde/derive", Some("serde")),
|
|
("makepad_micro_serde_derive_impl", "libs/micro_serde/derive_impl", None),
|
|
("makepad_script_derive", "platform/script/derive", Some("script")),
|
|
("makepad_script_derive_impl", "platform/script/derive_impl", None),
|
|
("makepad_derive_widget", "widgets/derive_widget", Some("widget")),
|
|
("makepad_derive_widget_impl", "widgets/derive_widget_impl", None),
|
|
("makepad_relax_api", "libs/relax_api", None),
|
|
("makepad_relax_api_macro", "libs/relax_api/macro", Some("relax_api")),
|
|
("makepad_relax_api_macro_impl", "libs/relax_api/macro_impl", None),
|
|
];
|
|
|
|
fn ident(token: Option<&TokenTree>, name: &str) -> bool {
|
|
matches!(token, Some(TokenTree::Ident(i)) if i.to_string() == name)
|
|
}
|
|
|
|
/// Adapt the actual wrapper source at every build. Only the proc-macro annotations and
|
|
/// token import change; wrapper bodies cannot drift from the embedded source identity.
|
|
fn bindings(source: &[u8], krate: &str, module: &str, entries: &mut String) -> String {
|
|
let mut flat = Vec::new();
|
|
relax_lex::lexer::lex(source, 2021, &mut flat).unwrap_or_else(|_| panic!("cannot lex proc-macro wrapper in {krate}"));
|
|
let stream = relax_lex::tokens::from_toks(source, &flat, &|_, _| Span::DUMMY).expect("parse proc-macro wrapper");
|
|
let tokens: Vec<_> = stream.into_iter().collect();
|
|
let mut adapted = Vec::new();
|
|
let mut i = 0;
|
|
while i < tokens.len() {
|
|
if matches!(&tokens[i], TokenTree::Punct(p) if p.as_char() == '#') {
|
|
if let Some(TokenTree::Group(attr)) = tokens.get(i + 1) {
|
|
if attr.delimiter() == Delimiter::Bracket {
|
|
let first = attr.stream().into_iter().next();
|
|
if ["proc_macro", "proc_macro_derive", "proc_macro_attribute"].iter().any(|name| ident(first.as_ref(), name)) {
|
|
assert!(ident(tokens.get(i + 2), "pub") && ident(tokens.get(i + 3), "fn"), "unsupported proc-macro wrapper in {krate}");
|
|
let Some(TokenTree::Ident(fname)) = tokens.get(i + 4) else { panic!("missing proc-macro name in {krate}") };
|
|
let kind = if ident(first.as_ref(), "proc_macro_attribute") { "Attribute" } else { "Unary" };
|
|
let declaration_name = format!("declaration_{fname}");
|
|
let declaration = if tokens.windows(2).any(|pair| ident(pair.first(), "fn") && ident(pair.get(1), &declaration_name)) {
|
|
format!("Some(Function::{kind}({module}::{declaration_name}))")
|
|
} else { "None".into() };
|
|
entries.push_str(&format!("Entry {{ krate: {krate:?}, fname: {:?}, function: Function::{kind}({module}::{fname}), declaration: {declaration} }},\n", fname.to_string()));
|
|
i += 2;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if ident(tokens.get(i), "extern") && ident(tokens.get(i + 1), "crate") && ident(tokens.get(i + 2), "proc_macro") {
|
|
assert!(matches!(tokens.get(i + 3), Some(TokenTree::Punct(p)) if p.as_char() == ';'), "unsupported proc_macro import in {krate}");
|
|
i += 4;
|
|
continue;
|
|
}
|
|
if ident(tokens.get(i), "fn") && matches!(tokens.get(i + 1), Some(TokenTree::Ident(name)) if name.to_string().starts_with("declaration_")) {
|
|
adapted.push(TokenTree::Ident(relax_lex::tokens::Ident::new("pub", Span::DUMMY)));
|
|
}
|
|
adapted.push(tokens[i].clone());
|
|
i += 1;
|
|
}
|
|
let adapted: TokenStream = adapted.into_iter().collect();
|
|
format!("mod {module} {{ use makepad_micro_proc_macro::proc_macro; {adapted} }}\n")
|
|
}
|
|
|
|
fn main() {
|
|
println!("cargo:rerun-if-changed=build.rs");
|
|
println!("cargo:rerun-if-changed=../build_sources.rs");
|
|
println!("cargo:rerun-if-changed=src/native_cfg.rs");
|
|
let makepad = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("../../..").canonicalize().expect("Makepad checkout");
|
|
let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
|
|
let mut packages = String::from("pub static PACKAGES: &[(&str, &[(&str, &[u8])])] = &[\n");
|
|
let mut wrappers = String::from("use super::{Entry, Function};\n");
|
|
let mut entries = String::new();
|
|
let mut divergent = Vec::new();
|
|
for (name, relative, module) in PACKAGES {
|
|
let root = makepad.join(relative);
|
|
packages.push_str(&format!("({name:?}, &[\n"));
|
|
let files = build_sources::package(&root);
|
|
divergent.extend(native_cfg::check_package(name, &files));
|
|
for (path, data) in files {
|
|
packages.push_str(&format!("({path:?}, include_bytes!({:?})),\n", root.join(&path)));
|
|
if path == "src/lib.rs" {
|
|
if let Some(module) = module {
|
|
wrappers.push_str(&bindings(&data, name, module, &mut entries));
|
|
}
|
|
}
|
|
}
|
|
packages.push_str("]),\n");
|
|
}
|
|
packages.push_str("];\n");
|
|
// source identity certifies behaviour only when no cfg differs between this copy and rustc's
|
|
assert!(divergent.is_empty(), "native proc-macro sources would expand differently under relax than under cargo:\n{}", divergent.join("\n"));
|
|
wrappers.push_str(&format!("pub static ENTRIES: &[Entry] = &[\n{entries}];\n"));
|
|
std::fs::write(out.join("sources.rs"), packages).expect("write native source identity");
|
|
std::fs::write(out.join("bindings.rs"), wrappers).expect("write native wrappers");
|
|
}
|