summaryrefslogtreecommitdiff
path: root/helix-syntax/build.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-syntax/build.rs')
-rw-r--r--helix-syntax/build.rs199
1 files changed, 133 insertions, 66 deletions
diff --git a/helix-syntax/build.rs b/helix-syntax/build.rs
index 847f8a67..02c4bc0a 100644
--- a/helix-syntax/build.rs
+++ b/helix-syntax/build.rs
@@ -1,79 +1,147 @@
+use anyhow::{anyhow, Context, Result};
use std::fs;
-use std::path::PathBuf;
+use std::time::SystemTime;
+use std::{
+ path::{Path, PathBuf},
+ process::Command,
+};
use std::sync::mpsc::channel;
-fn collect_tree_sitter_dirs(ignore: &[String]) -> Vec<String> {
+fn collect_tree_sitter_dirs(ignore: &[String]) -> Result<Vec<String>> {
let mut dirs = Vec::new();
- for entry in fs::read_dir("languages").unwrap().flatten() {
+ let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("languages");
+
+ for entry in fs::read_dir(path)? {
+ let entry = entry?;
let path = entry.path();
- let dir = path.file_name().unwrap().to_str().unwrap().to_string();
- if !ignore.contains(&dir) {
- dirs.push(dir);
+
+ if !entry.file_type()?.is_dir() {
+ continue;
}
- }
- dirs
-}
-fn collect_src_files(dir: &str) -> (Vec<String>, Vec<String>) {
- eprintln!("Collect files for {}", dir);
+ let dir = path.file_name().unwrap().to_str().unwrap().to_string();
- let mut c_files = Vec::new();
- let mut cpp_files = Vec::new();
- let path = PathBuf::from("languages").join(&dir).join("src");
- for entry in fs::read_dir(path).unwrap().flatten() {
- let path = entry.path();
- if path
- .file_stem()
- .unwrap()
- .to_str()
- .unwrap()
- .starts_with("binding")
- {
+ // filter ignores
+ if ignore.contains(&dir) {
continue;
}
- if let Some(ext) = path.extension() {
- if ext == "c" {
- c_files.push(path.to_str().unwrap().to_string());
- } else if ext == "cc" || ext == "cpp" || ext == "cxx" {
- cpp_files.push(path.to_str().unwrap().to_string());
- }
- }
+ dirs.push(dir)
}
- (c_files, cpp_files)
-}
-fn build_c(files: Vec<String>, language: &str) {
- let mut build = cc::Build::new();
- for file in files {
- build
- .file(&file)
- .include(PathBuf::from(file).parent().unwrap())
- .pic(true)
- .warnings(false);
- }
- build.compile(&format!("tree-sitter-{}-c", language));
+ Ok(dirs)
}
-fn build_cpp(files: Vec<String>, language: &str) {
- let mut build = cc::Build::new();
+#[cfg(unix)]
+const DYLIB_EXTENSION: &str = "so";
+
+#[cfg(windows)]
+const DYLIB_EXTENSION: &str = "dll";
- let flag = if build.get_compiler().is_like_msvc() {
- "/std:c++17"
+fn build_library(src_path: &Path, language: &str) -> Result<()> {
+ let header_path = src_path;
+ // let grammar_path = src_path.join("grammar.json");
+ let parser_path = src_path.join("parser.c");
+ let mut scanner_path = src_path.join("scanner.c");
+
+ let scanner_path = if scanner_path.exists() {
+ Some(scanner_path)
} else {
- "-std=c++14"
+ scanner_path.set_extension("cc");
+ if scanner_path.exists() {
+ Some(scanner_path)
+ } else {
+ None
+ }
};
+ let parser_lib_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../runtime/grammars");
+ let mut library_path = parser_lib_path.join(language);
+ library_path.set_extension(DYLIB_EXTENSION);
- for file in files {
- build
- .file(&file)
- .include(PathBuf::from(file).parent().unwrap())
- .pic(true)
- .warnings(false)
- .cpp(true)
- .flag_if_supported(flag);
+ let recompile = needs_recompile(&library_path, &parser_path, &scanner_path)
+ .with_context(|| "Failed to compare source and binary timestamps")?;
+
+ if !recompile {
+ return Ok(());
+ }
+ let mut config = cc::Build::new();
+ config.cpp(true).opt_level(2).cargo_metadata(false);
+ let compiler = config.get_compiler();
+ let mut command = Command::new(compiler.path());
+ command.current_dir(src_path);
+ for (key, value) in compiler.env() {
+ command.env(key, value);
}
- build.compile(&format!("tree-sitter-{}-cpp", language));
+
+ if cfg!(windows) {
+ command
+ .args(&["/nologo", "/LD", "/I"])
+ .arg(header_path)
+ .arg("/Od");
+ if let Some(scanner_path) = scanner_path.as_ref() {
+ command.arg(scanner_path);
+ }
+
+ command
+ .arg(parser_path)
+ .arg("/link")
+ .arg(format!("/out:{}", library_path.to_str().unwrap()));
+ } else {
+ command
+ .arg("-shared")
+ .arg("-fPIC")
+ .arg("-fno-exceptions")
+ .arg("-g")
+ .arg("-I")
+ .arg(header_path)
+ .arg("-o")
+ .arg(&library_path)
+ .arg("-O2");
+ if let Some(scanner_path) = scanner_path.as_ref() {
+ if scanner_path.extension() == Some("c".as_ref()) {
+ command.arg("-xc").arg("-std=c99").arg(scanner_path);
+ } else {
+ command.arg(scanner_path);
+ }
+ }
+ command.arg("-xc").arg(parser_path);
+ }
+
+ let output = command
+ .output()
+ .with_context(|| "Failed to execute C compiler")?;
+ if !output.status.success() {
+ return Err(anyhow!(
+ "Parser compilation failed.\nStdout: {}\nStderr: {}",
+ String::from_utf8_lossy(&output.stdout),
+ String::from_utf8_lossy(&output.stderr)
+ ));
+ }
+
+ Ok(())
+}
+fn needs_recompile(
+ lib_path: &Path,
+ parser_c_path: &Path,
+ scanner_path: &Option<PathBuf>,
+) -> Result<bool> {
+ if !lib_path.exists() {
+ return Ok(true);
+ }
+ let lib_mtime = mtime(lib_path)?;
+ if mtime(parser_c_path)? > lib_mtime {
+ return Ok(true);
+ }
+ if let Some(scanner_path) = scanner_path {
+ if mtime(scanner_path)? > lib_mtime {
+ return Ok(true);
+ }
+ }
+ Ok(false)
+}
+
+fn mtime(path: &Path) -> Result<SystemTime> {
+ Ok(fs::metadata(path)?.modified()?)
}
fn build_dir(dir: &str, language: &str) {
@@ -92,22 +160,21 @@ fn build_dir(dir: &str, language: &str) {
eprintln!("You can fix in using 'git submodule init && git submodule update --recursive'.");
std::process::exit(1);
}
- let (c, cpp) = collect_src_files(dir);
- if !c.is_empty() {
- build_c(c, language);
- }
- if !cpp.is_empty() {
- build_cpp(cpp, language);
- }
+
+ let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
+ .join("languages")
+ .join(dir)
+ .join("src");
+
+ build_library(&path, language).unwrap();
}
fn main() {
let ignore = vec![
"tree-sitter-typescript".to_string(),
"tree-sitter-haskell".to_string(), // aarch64 failures: https://github.com/tree-sitter/tree-sitter-haskell/issues/34
- ".DS_Store".to_string(),
];
- let dirs = collect_tree_sitter_dirs(&ignore);
+ let dirs = collect_tree_sitter_dirs(&ignore).unwrap();
let mut n_jobs = 0;
let pool = threadpool::Builder::new().build(); // by going through the builder, it'll use num_cpus
@@ -118,7 +185,7 @@ fn main() {
n_jobs += 1;
pool.execute(move || {
- let language = &dir[12..]; // skip tree-sitter- prefix
+ let language = &dir.strip_prefix("tree-sitter-").unwrap();
build_dir(&dir, language);
// report progress