@@ -1,5 +1,7 @@
1use std::fs;
2use std::fs;
2use std::path::PathBuf;
3use std::path::PathBuf;
4+use std::time::SystemTime;
4use std::sync::mpsc::channel;
6use std::sync::mpsc::channel;
@@ -15,66 +17,156 @@ fn collect_tree_sitter_dirs(ignore: &[String]) -> Vec<String> {
18−fn collect_src_files(dir: &str) -> (Vec<String>, Vec<String>) {
20+#[cfg(unix)]
19− eprintln!("Collect files for {}", dir);
21+const DYLIB_EXTENSION: &str = "so";
21− let mut c_files = Vec::new();
23+#[cfg(windows)]
22− let mut cpp_files = Vec::new();
24+const DYLIB_EXTENSION: &str = "dll";
23− let path = PathBuf::from("languages").join(&dir).join("src");
25+
24− for entry in fs::read_dir(path).unwrap().flatten() {
26+// const BUILD_TARGET: &'static str = env!("BUILD_TARGET");
25− let path = entry.path();
27+
26− if path
28+use anyhow::{anyhow, Context};
27− .file_stem()
29+use std::{path::Path, process::Command};
29− .to_str()
31+fn build_library(src_path: &Path, language: &str) -> Result<()> {
30− .unwrap()
32+ let header_path = src_path;
31− .starts_with("binding")
33+ // let grammar_path = src_path.join("grammar.json");
32− {
34+ let parser_path = src_path.join("parser.c");
33− continue;
35+ let mut scanner_path = src_path.join("scanner.c");
37+ let scanner_path = if scanner_path.exists() {
40+ scanner_path.set_extension("cc");
41+ if scanner_path.exists() {
47+ let parser_lib_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../runtime/grammars");
48+ let mut library_path = parser_lib_path.join(language);
49+ library_path.set_extension(DYLIB_EXTENSION);
51+ let recompile = needs_recompile(&library_path, &parser_path, &scanner_path)
52+ .with_context(|| "Failed to compare source and binary timestamps")?;
58+ let mut config = cc::Build::new();
59+ config.cpp(true).opt_level(2).cargo_metadata(false);
60+ // .target(BUILD_TARGET)
61+ // .host(BUILD_TARGET);
62+ let compiler = config.get_compiler();
63+ let mut command = Command::new(compiler.path());
64+ for (key, value) in compiler.env() {
65+ command.env(key, value);
70+ .args(&["/nologo", "/LD", "/I"])
74+ if let Some(scanner_path) = scanner_path.as_ref() {
75+ command.arg(scanner_path);
35− if let Some(ext) = path.extension() {
77+ command
36− if ext == "c" {
78+ .arg("/link")
37− c_files.push(path.to_str().unwrap().to_string());
79+ .arg(format!("/out:{}", library_path.to_str().unwrap()));
38− } else if ext == "cc" || ext == "cpp" || ext == "cxx" {
80+ } else {
39− cpp_files.push(path.to_str().unwrap().to_string());
81+ command
84+ .arg("-fno-exceptions")
91+ if let Some(scanner_path) = scanner_path.as_ref() {
92+ if scanner_path.extension() == Some("c".as_ref()) {
93+ command.arg("-xc").arg("-std=c99").arg(scanner_path);
95+ command.arg(scanner_path);
98+ command.arg("-xc").arg(parser_path);
46−fn build_c(files: Vec<String>, language: &str) {
101+ let output = command
47− let mut build = cc::Build::new();
102+ .output()
48− for file in files {
103+ .with_context(|| "Failed to execute C compiler")?;
49− build
104+ if !output.status.success() {
50− .file(&file)
105+ return Err(anyhow!(
51− .include(PathBuf::from(file).parent().unwrap())
106+ "Parser compilation failed.\nStdout: {}\nStderr: {}",
52− .pic(true)
107+ String::from_utf8_lossy(&output.stdout),
53− .warnings(false);
108+ String::from_utf8_lossy(&output.stderr)
55− build.compile(&format!("tree-sitter-{}-c", language));
111+
116+ parser_c_path: &Path,
117+ scanner_path: &Option<PathBuf>,
119+ if !lib_path.exists() {
122+ let lib_mtime = mtime(lib_path)?;
123+ if mtime(parser_c_path)? > lib_mtime {
126+ if let Some(scanner_path) = scanner_path {
127+ if mtime(scanner_path)? > lib_mtime {
58−fn build_cpp(files: Vec<String>, language: &str) {
134+fn mtime(path: &Path) -> Result<SystemTime> {
59− let mut build = cc::Build::new();
135+ Ok(fs::metadata(path)?.modified()?)
61− let flag = if build.get_compiler().is_like_msvc() {
138+// fn build_c(files: Vec<String>, language: &str) {
62− "/std:c++17"
139+// let mut build = cc::Build::new();
63− } else {
140+// for file in files {
64− "-std=c++14"
141+// build
143+// .include(PathBuf::from(file).parent().unwrap())
147+// build.compile(&format!("tree-sitter-{}-c", language));
150+// fn build_cpp(files: Vec<String>, language: &str) {
151+// let mut build = cc::Build::new();
153+// let flag = if build.get_compiler().is_like_msvc() {
67− for file in files {
159+// for file in files {
69− .file(&file)
161+// .file(&file)
70− .include(PathBuf::from(file).parent().unwrap())
162+// .include(PathBuf::from(file).parent().unwrap())
71− .pic(true)
163+// .pic(true)
72− .warnings(false)
164+// .warnings(false)
73− .cpp(true)
165+// .cpp(true)
74− .flag_if_supported(flag);
166+// .flag_if_supported(flag);
76− build.compile(&format!("tree-sitter-{}-cpp", language));
168+// build.compile(&format!("tree-sitter-{}-cpp", language));
79fn build_dir(dir: &str, language: &str) {
171fn build_dir(dir: &str, language: &str) {
80 println!("Build language {}", language);
172 println!("Build language {}", language);
@@ -92,13 +184,9 @@ fn build_dir(dir: &str, language: &str) {
92 eprintln!("You can fix in using 'git submodule init && git submodule update --recursive'.");
184 eprintln!("You can fix in using 'git submodule init && git submodule update --recursive'.");
93 std::process::exit(1);
185 std::process::exit(1);
95− let (c, cpp) = collect_src_files(dir);
187+
96− if !c.is_empty() {
188+ let path = Path::new("languages").join(dir).join("src");
97− build_c(c, language);
189+ build_library(&path, language).unwrap();
100− build_cpp(cpp, language);
104fn main() {
192fn main() {
@@ -129,6 +217,6 @@ fn main() {
129 // drop(tx);
217 // drop(tx);
130 assert_eq!(rx.try_iter().sum::<usize>(), n_jobs);
218 assert_eq!(rx.try_iter().sum::<usize>(), n_jobs);
132− build_dir("tree-sitter-typescript/tsx", "tsx");
220+ // build_dir("tree-sitter-typescript/tsx", "tsx");
133− build_dir("tree-sitter-typescript/typescript", "typescript");
221+ // build_dir("tree-sitter-typescript/typescript", "typescript");