use std::{ env, fs, path::{Path, PathBuf}, process::Command, }; fn main() -> Result<(), Box> { println!("cargo:rerun-if-env-changed=SYNTASTICA_PARSERS_CLONE_DIR"); println!("cargo:rerun-if-env-changed=SYNTASTICA_PARSERS_CACHE_DIR"); if let Ok(dir) = env::var("SYNTASTICA_PARSERS_CLONE_DIR") { println!("cargo:rerun-if-changed={dir}") } if let Ok(dir) = env::var("SYNTASTICA_PARSERS_CACHE_DIR") { println!("cargo:rerun-if-changed={dir}") } if !(cfg!(feature = "docs") && env::var("DOCS_RS").is_ok()) { syntastica_macros::parsers_git!(); } // for documenting features when using nightly #[cfg(feature = "docs")] { let channel = match rustc_version::version_meta().unwrap().channel { rustc_version::Channel::Dev => "CHANNEL_DEV", rustc_version::Channel::Nightly => "CHANNEL_NIGHTLY", rustc_version::Channel::Beta => "CHANNEL_BETA", rustc_version::Channel::Stable => "CHANNEL_STABLE", }; println!("cargo:rustc-cfg={channel}"); } println!("cargo:rerun-if-changed=build.rs"); Ok(()) } #[allow(unused)] fn git(repo_dir: &Path) -> Command { let mut cmd = Command::new("git"); cmd.current_dir(repo_dir) .stdout(std::process::Stdio::inherit()) .stderr(std::process::Stdio::inherit()); cmd } #[allow(unused)] fn compile_parser( name: &str, url: &str, rev: &str, external_c: bool, external_cpp: bool, path: Option<&str>, wasm: bool, ) -> Result<(), Box> { let target = env::var("TARGET")?; // some parsers are not supported for wasm targets if env::var("CARGO_CFG_TARGET_FAMILY")? .split(',') .any(|family| family == "wasm") && !wasm { return Ok(()); } // external cpp scanners are not supported on the `wasm32-unknown-unknown` target if target == "wasm32-unknown-unknown" && external_cpp { return Ok(()); } let out_dir = PathBuf::from(env::var("OUT_DIR")?); let clone_dir = env::var("SYNTASTICA_PARSERS_CLONE_DIR") .map_or_else(|_| out_dir.join("clone"), PathBuf::from); let c_lib_name = format!("parser_{name}{}_{rev}", path.unwrap_or("")).replace('/', "_"); let c_lib_filename = format!("lib{c_lib_name}.a"); let cpp_lib_name = format!("scanner_{name}{}_{rev}", path.unwrap_or("")).replace('/', "_"); let cpp_lib_filename = format!("lib{cpp_lib_name}.a"); if let Ok(dir) = env::var("SYNTASTICA_PARSERS_CACHE_DIR") { if Path::new(&dir) .join(&target) .join(&c_lib_filename) .is_file() && (!external_cpp || Path::new(&dir) .join(&target) .join(&cpp_lib_filename) .is_file()) { println!("cargo:rustc-link-lib=static={c_lib_name}"); if external_cpp { println!("cargo:rustc-link-lib=static={cpp_lib_name}"); println!("cargo:rustc-link-lib=stdc++"); } println!( "cargo:rustc-link-search=native={}", Path::new(&dir).join(&target).canonicalize()?.display() ); return Ok(()); } } // clone repo into `parsers/{name}`, if it does not already exists let repo_dir = clone_dir.join(name).join(rev); if !repo_dir.exists() { println!("cloning repository for {name}"); fs::create_dir_all(&repo_dir)?; git(&repo_dir).arg("init").status()?; git(&repo_dir) .args(["remote", "add", "origin", url]) .status()?; git(&repo_dir) .args(["fetch", "--depth=1", "origin", rev]) .status()?; git(&repo_dir).args(["checkout", "FETCH_HEAD"]).status()?; } let mut src_dir = repo_dir; if let Some(path) = path { src_dir = src_dir.join(path); } src_dir = src_dir.join("src"); println!("building parser for {name}"); let mut c_config = cc::Build::new(); c_config.include(&src_dir); c_config .flag_if_supported("-Wno-unused-parameter") .flag_if_supported("-Wno-unused-but-set-variable") .flag_if_supported("-Wno-trigraphs") .flag_if_supported("-w"); let parser_path = src_dir.join("parser.c"); c_config.file(&parser_path); if external_c { let scanner_path = src_dir.join("scanner.c"); c_config.file(&scanner_path); println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); } #[cfg(feature = "runtime-c2rust")] tree_sitter_wasm_build_tool::add_wasm_headers(&mut c_config).unwrap(); println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); c_config.compile(&c_lib_name); println!("finished building parser for {name}"); if let Ok(dir) = env::var("SYNTASTICA_PARSERS_CACHE_DIR") { let cache_dir = Path::new(&dir).join(&target); fs::create_dir_all(&cache_dir)?; fs::copy( out_dir.join(&c_lib_filename), cache_dir.join(&c_lib_filename), )?; } if external_cpp { println!("building cpp scanner for {name}"); let mut cpp_config = cc::Build::new(); cpp_config.cpp(true); cpp_config.include(&src_dir); cpp_config .flag_if_supported("-Wno-unused-parameter") .flag_if_supported("-Wno-unused-but-set-variable") .flag_if_supported("-w"); let scanner_path = src_dir.join("scanner.cc"); cpp_config.file(&scanner_path); println!("cargo:rerun-if-changed={}", scanner_path.to_str().unwrap()); cpp_config.compile(&cpp_lib_name); println!("finished building cpp scanner for {name}"); if let Ok(dir) = env::var("SYNTASTICA_PARSERS_CACHE_DIR") { fs::copy( out_dir.join(&cpp_lib_filename), Path::new(&dir).join(&target).join(&cpp_lib_filename), )?; } } Ok(()) }