@@ -1,5 +1,7 @@
1+use anyhow::Result;
12use std::fs;
23use std::path::PathBuf;
4+use std::time::SystemTime;
35
46use std::sync::mpsc::channel;
57
@@ -15,66 +17,156 @@ fn collect_tree_sitter_dirs(ignore: &[String]) -> Vec<String> {
1517 dirs
1618}
1719
18−fn collect_src_files(dir: &str) -> (Vec<String>, Vec<String>) {
19− eprintln!("Collect files for {}", dir);
20+#[cfg(unix)]
21+const DYLIB_EXTENSION: &str = "so";
2022
21− let mut c_files = Vec::new();
22− let mut cpp_files = Vec::new();
23− let path = PathBuf::from("languages").join(&dir).join("src");
24− for entry in fs::read_dir(path).unwrap().flatten() {
25− let path = entry.path();
26− if path
27− .file_stem()
28− .unwrap()
29− .to_str()
30− .unwrap()
31− .starts_with("binding")
32− {
33− continue;
23+#[cfg(windows)]
24+const DYLIB_EXTENSION: &str = "dll";
25+
26+// const BUILD_TARGET: &'static str = env!("BUILD_TARGET");
27+
28+use anyhow::{anyhow, Context};
29+use std::{path::Path, process::Command};
30+
31+fn build_library(src_path: &Path, language: &str) -> Result<()> {
32+ let header_path = src_path;
33+ // let grammar_path = src_path.join("grammar.json");
34+ let parser_path = src_path.join("parser.c");
35+ let mut scanner_path = src_path.join("scanner.c");
36+
37+ let scanner_path = if scanner_path.exists() {
38+ Some(scanner_path)
39+ } else {
40+ scanner_path.set_extension("cc");
41+ if scanner_path.exists() {
42+ Some(scanner_path)
43+ } else {
44+ None
45+ }
46+ };
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);
50+
51+ let recompile = needs_recompile(&library_path, &parser_path, &scanner_path)
52+ .with_context(|| "Failed to compare source and binary timestamps")?;
53+
54+ if !recompile {
55+ return Ok(());
56+ }
57+
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);
66+ }
67+
68+ if cfg!(windows) {
69+ command
70+ .args(&["/nologo", "/LD", "/I"])
71+ .arg(header_path)
72+ .arg("/Od")
73+ .arg(parser_path);
74+ if let Some(scanner_path) = scanner_path.as_ref() {
75+ command.arg(scanner_path);
3476 }
35− if let Some(ext) = path.extension() {
36− if ext == "c" {
37− c_files.push(path.to_str().unwrap().to_string());
38− } else if ext == "cc" || ext == "cpp" || ext == "cxx" {
39− cpp_files.push(path.to_str().unwrap().to_string());
77+ command
78+ .arg("/link")
79+ .arg(format!("/out:{}", library_path.to_str().unwrap()));
80+ } else {
81+ command
82+ .arg("-shared")
83+ .arg("-fPIC")
84+ .arg("-fno-exceptions")
85+ .arg("-g")
86+ .arg("-I")
87+ .arg(header_path)
88+ .arg("-o")
89+ .arg(&library_path)
90+ .arg("-O2");
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);
94+ } else {
95+ command.arg(scanner_path);
4096 }
4197 }
98+ command.arg("-xc").arg(parser_path);
4299 }
43− (c_files, cpp_files)
44−}
45100
46−fn build_c(files: Vec<String>, language: &str) {
47− let mut build = cc::Build::new();
48− for file in files {
49− build
50− .file(&file)
51− .include(PathBuf::from(file).parent().unwrap())
52− .pic(true)
53− .warnings(false);
101+ let output = command
102+ .output()
103+ .with_context(|| "Failed to execute C compiler")?;
104+ if !output.status.success() {
105+ return Err(anyhow!(
106+ "Parser compilation failed.\nStdout: {}\nStderr: {}",
107+ String::from_utf8_lossy(&output.stdout),
108+ String::from_utf8_lossy(&output.stderr)
109+ ));
54110 }
55− build.compile(&format!("tree-sitter-{}-c", language));
111+
112+ Ok(())
113+}
114+fn needs_recompile(
115+ lib_path: &Path,
116+ parser_c_path: &Path,
117+ scanner_path: &Option<PathBuf>,
118+) -> Result<bool> {
119+ if !lib_path.exists() {
120+ return Ok(true);
121+ }
122+ let lib_mtime = mtime(lib_path)?;
123+ if mtime(parser_c_path)? > lib_mtime {
124+ return Ok(true);
125+ }
126+ if let Some(scanner_path) = scanner_path {
127+ if mtime(scanner_path)? > lib_mtime {
128+ return Ok(true);
129+ }
130+ }
131+ Ok(false)
56132}
57133
58−fn build_cpp(files: Vec<String>, language: &str) {
59− let mut build = cc::Build::new();
134+fn mtime(path: &Path) -> Result<SystemTime> {
135+ Ok(fs::metadata(path)?.modified()?)
136+}
60137
61− let flag = if build.get_compiler().is_like_msvc() {
62− "/std:c++17"
63− } else {
64− "-std=c++14"
65− };
138+// fn build_c(files: Vec<String>, language: &str) {
139+// let mut build = cc::Build::new();
140+// for file in files {
141+// build
142+// .file(&file)
143+// .include(PathBuf::from(file).parent().unwrap())
144+// .pic(true)
145+// .warnings(false);
146+// }
147+// build.compile(&format!("tree-sitter-{}-c", language));
148+// }
66149
67− for file in files {
68− build
69− .file(&file)
70− .include(PathBuf::from(file).parent().unwrap())
71− .pic(true)
72− .warnings(false)
73− .cpp(true)
74− .flag_if_supported(flag);
75− }
76− build.compile(&format!("tree-sitter-{}-cpp", language));
77−}
150+// fn build_cpp(files: Vec<String>, language: &str) {
151+// let mut build = cc::Build::new();
152+
153+// let flag = if build.get_compiler().is_like_msvc() {
154+// "/std:c++17"
155+// } else {
156+// "-std=c++14"
157+// };
158+
159+// for file in files {
160+// build
161+// .file(&file)
162+// .include(PathBuf::from(file).parent().unwrap())
163+// .pic(true)
164+// .warnings(false)
165+// .cpp(true)
166+// .flag_if_supported(flag);
167+// }
168+// build.compile(&format!("tree-sitter-{}-cpp", language));
169+// }
78170
79171fn build_dir(dir: &str, language: &str) {
80172 println!("Build language {}", language);
@@ -92,13 +184,9 @@ fn build_dir(dir: &str, language: &str) {
92184 eprintln!("You can fix in using 'git submodule init && git submodule update --recursive'.");
93185 std::process::exit(1);
94186 }
95− let (c, cpp) = collect_src_files(dir);
96− if !c.is_empty() {
97− build_c(c, language);
98− }
99− if !cpp.is_empty() {
100− build_cpp(cpp, language);
101− }
187+
188+ let path = Path::new("languages").join(dir).join("src");
189+ build_library(&path, language).unwrap();
102190}
103191
104192fn main() {
@@ -129,6 +217,6 @@ fn main() {
129217 // drop(tx);
130218 assert_eq!(rx.try_iter().sum::<usize>(), n_jobs);
131219
132− build_dir("tree-sitter-typescript/tsx", "tsx");
133− build_dir("tree-sitter-typescript/typescript", "typescript");
220+ // build_dir("tree-sitter-typescript/tsx", "tsx");
221+ // build_dir("tree-sitter-typescript/typescript", "typescript");
134222}