2019-04-23 14:29:46 -07:00
|
|
|
use super::generate::parse_grammar::GrammarJSON;
|
2021-06-09 12:32:22 -04:00
|
|
|
use anyhow::{anyhow, Context, Result};
|
2022-01-03 10:57:01 -08:00
|
|
|
use std::{
|
|
|
|
|
ffi::{OsStr, OsString},
|
|
|
|
|
fs,
|
|
|
|
|
path::Path,
|
|
|
|
|
process::Command,
|
|
|
|
|
};
|
2021-04-04 19:07:16 -05:00
|
|
|
use which::which;
|
2019-04-23 14:29:46 -07:00
|
|
|
|
2021-06-29 21:11:21 +00:00
|
|
|
const EMSCRIPTEN_TAG: &'static str = concat!("emscripten/emsdk:", env!("EMSCRIPTEN_VERSION"));
|
|
|
|
|
|
2022-01-03 10:57:01 -08:00
|
|
|
pub fn load_language_wasm_file(language_dir: &Path) -> Result<(String, Vec<u8>)> {
|
|
|
|
|
let grammar_name = get_grammar_name(&language_dir)
|
|
|
|
|
.with_context(|| "Failed to get wasm filename")
|
|
|
|
|
.unwrap();
|
|
|
|
|
let wasm_filename = format!("tree-sitter-{}.wasm", grammar_name);
|
|
|
|
|
let contents = fs::read(language_dir.join(&wasm_filename)).with_context(|| {
|
|
|
|
|
format!(
|
|
|
|
|
"Failed to read {}. Run `tree-sitter build-wasm` first.",
|
|
|
|
|
wasm_filename
|
|
|
|
|
)
|
|
|
|
|
})?;
|
|
|
|
|
Ok((grammar_name, contents))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_grammar_name(language_dir: &Path) -> Result<String> {
|
|
|
|
|
let src_dir = language_dir.join("src");
|
2019-04-23 14:29:46 -07:00
|
|
|
let grammar_json_path = src_dir.join("grammar.json");
|
2021-06-09 12:32:22 -04:00
|
|
|
let grammar_json = fs::read_to_string(&grammar_json_path)
|
|
|
|
|
.with_context(|| format!("Failed to read grammar file {:?}", grammar_json_path))?;
|
|
|
|
|
let grammar: GrammarJSON = serde_json::from_str(&grammar_json)
|
|
|
|
|
.with_context(|| format!("Failed to parse grammar file {:?}", grammar_json_path))?;
|
2019-05-13 21:51:17 -07:00
|
|
|
Ok(grammar.name)
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-14 11:12:56 -07:00
|
|
|
pub fn compile_language_to_wasm(language_dir: &Path, force_docker: bool) -> Result<()> {
|
2022-01-03 10:57:01 -08:00
|
|
|
let grammar_name = get_grammar_name(&language_dir)?;
|
2019-05-13 21:51:17 -07:00
|
|
|
let output_filename = format!("tree-sitter-{}.wasm", grammar_name);
|
2019-04-25 17:27:24 -07:00
|
|
|
|
2021-05-23 13:53:07 +03:00
|
|
|
let emcc_bin = if cfg!(windows) { "emcc.bat" } else { "emcc" };
|
|
|
|
|
let emcc_path = which(emcc_bin)
|
|
|
|
|
.ok()
|
|
|
|
|
.and_then(|p| Command::new(&p).output().and(Ok(p)).ok());
|
|
|
|
|
|
2019-05-14 11:12:56 -07:00
|
|
|
let mut command;
|
2021-05-23 13:53:07 +03:00
|
|
|
if !force_docker && emcc_path.is_some() {
|
|
|
|
|
command = Command::new(emcc_path.unwrap());
|
|
|
|
|
command.current_dir(&language_dir);
|
2019-06-10 11:40:36 -07:00
|
|
|
} else if Command::new("docker").output().is_ok() {
|
2019-05-14 11:12:56 -07:00
|
|
|
command = Command::new("docker");
|
|
|
|
|
command.args(&["run", "--rm"]);
|
|
|
|
|
|
|
|
|
|
// Mount the parser directory as a volume
|
2019-05-30 12:34:03 -07:00
|
|
|
let mut volume_string;
|
|
|
|
|
if let (Some(parent), Some(filename)) = (language_dir.parent(), language_dir.file_name()) {
|
|
|
|
|
volume_string = OsString::from(parent);
|
2019-06-29 15:49:25 -04:00
|
|
|
volume_string.push(":/src:Z");
|
2019-05-30 12:34:03 -07:00
|
|
|
command.arg("--workdir");
|
|
|
|
|
command.arg(&Path::new("/src").join(filename));
|
|
|
|
|
} else {
|
|
|
|
|
volume_string = OsString::from(language_dir);
|
2019-06-29 15:49:25 -04:00
|
|
|
volume_string.push(":/src:Z");
|
2019-05-30 12:34:03 -07:00
|
|
|
command.args(&["--workdir", "/src"]);
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-14 11:12:56 -07:00
|
|
|
command.args(&[OsStr::new("--volume"), &volume_string]);
|
2019-04-23 14:29:46 -07:00
|
|
|
|
2019-05-14 11:12:56 -07:00
|
|
|
// Get the current user id so that files created in the docker container will have
|
|
|
|
|
// the same owner.
|
2019-06-10 11:40:36 -07:00
|
|
|
if cfg!(unix) {
|
|
|
|
|
let user_id_output = Command::new("id")
|
|
|
|
|
.arg("-u")
|
|
|
|
|
.output()
|
2021-06-09 12:32:22 -04:00
|
|
|
.with_context(|| "Failed to get get current user id")?;
|
2019-06-10 11:40:36 -07:00
|
|
|
let user_id = String::from_utf8_lossy(&user_id_output.stdout);
|
|
|
|
|
let user_id = user_id.trim();
|
|
|
|
|
command.args(&["--user", user_id]);
|
|
|
|
|
}
|
2019-05-14 11:12:56 -07:00
|
|
|
|
|
|
|
|
// Run `emcc` in a container using the `emscripten-slim` image
|
2021-06-29 21:11:21 +00:00
|
|
|
command.args(&[EMSCRIPTEN_TAG, "emcc"]);
|
2019-06-10 11:40:36 -07:00
|
|
|
} else {
|
2021-06-09 12:32:22 -04:00
|
|
|
return Err(anyhow!(
|
|
|
|
|
"You must have either emcc or docker on your PATH to run this command"
|
|
|
|
|
));
|
2019-05-14 11:12:56 -07:00
|
|
|
}
|
2019-04-25 17:27:24 -07:00
|
|
|
|
2019-04-23 14:29:46 -07:00
|
|
|
command.args(&[
|
|
|
|
|
"-o",
|
2019-04-25 17:27:24 -07:00
|
|
|
&output_filename,
|
2019-04-23 14:29:46 -07:00
|
|
|
"-Os",
|
|
|
|
|
"-s",
|
|
|
|
|
"WASM=1",
|
|
|
|
|
"-s",
|
|
|
|
|
"SIDE_MODULE=1",
|
|
|
|
|
"-s",
|
2019-04-27 17:54:00 -07:00
|
|
|
"TOTAL_MEMORY=33554432",
|
|
|
|
|
"-s",
|
2019-07-10 13:41:16 +01:00
|
|
|
"NODEJS_CATCH_EXIT=0",
|
|
|
|
|
"-s",
|
2022-10-01 13:03:23 -04:00
|
|
|
"NODEJS_CATCH_REJECTION=0",
|
|
|
|
|
"-s",
|
2019-05-13 21:51:17 -07:00
|
|
|
&format!("EXPORTED_FUNCTIONS=[\"_tree_sitter_{}\"]", grammar_name),
|
2019-05-01 11:29:35 -07:00
|
|
|
"-fno-exceptions",
|
2019-04-25 17:27:24 -07:00
|
|
|
"-I",
|
|
|
|
|
"src",
|
2019-04-23 14:29:46 -07:00
|
|
|
]);
|
|
|
|
|
|
2020-05-12 16:56:21 -07:00
|
|
|
let src = Path::new("src");
|
|
|
|
|
let parser_c_path = src.join("parser.c");
|
|
|
|
|
let scanner_c_path = src.join("scanner.c");
|
|
|
|
|
let scanner_cc_path = src.join("scanner.cc");
|
|
|
|
|
let scanner_cpp_path = src.join("scanner.cpp");
|
2019-04-23 14:29:46 -07:00
|
|
|
|
2020-05-13 15:14:43 -07:00
|
|
|
if language_dir.join(&scanner_cc_path).exists() {
|
2020-05-12 15:42:11 -07:00
|
|
|
command.arg("-xc++").arg(&scanner_cc_path);
|
2020-05-13 15:14:43 -07:00
|
|
|
} else if language_dir.join(&scanner_cpp_path).exists() {
|
2020-05-12 15:42:11 -07:00
|
|
|
command.arg("-xc++").arg(&scanner_cpp_path);
|
2020-05-13 15:14:43 -07:00
|
|
|
} else if language_dir.join(&scanner_c_path).exists() {
|
2020-05-12 15:42:11 -07:00
|
|
|
command.arg(&scanner_c_path);
|
2019-04-23 14:29:46 -07:00
|
|
|
}
|
|
|
|
|
|
2020-05-12 15:42:11 -07:00
|
|
|
command.arg(&parser_c_path);
|
|
|
|
|
|
2019-04-23 14:29:46 -07:00
|
|
|
let output = command
|
|
|
|
|
.output()
|
2021-06-09 12:32:22 -04:00
|
|
|
.with_context(|| "Failed to run emcc command")?;
|
2019-04-25 17:27:24 -07:00
|
|
|
if !output.status.success() {
|
2021-06-09 12:32:22 -04:00
|
|
|
return Err(anyhow!(
|
2019-04-23 14:29:46 -07:00
|
|
|
"emcc command failed - {}",
|
|
|
|
|
String::from_utf8_lossy(&output.stderr)
|
2021-06-09 12:32:22 -04:00
|
|
|
));
|
2019-04-23 14:29:46 -07:00
|
|
|
}
|
2019-04-25 17:27:24 -07:00
|
|
|
|
|
|
|
|
// Move the created `.wasm` file into the current working directory.
|
2021-06-09 12:32:22 -04:00
|
|
|
fs::rename(&language_dir.join(&output_filename), &output_filename)
|
|
|
|
|
.with_context(|| format!("Couldn't find output file {:?}", output_filename))?;
|
2019-04-25 17:27:24 -07:00
|
|
|
|
|
|
|
|
Ok(())
|
2019-04-23 14:29:46 -07:00
|
|
|
}
|