Skip to content

Commit 473b3c8

Browse files
committed
Add a CLI feature flag for wasm runtime support
1 parent 3380e0e commit 473b3c8

File tree

6 files changed

+56
-155
lines changed

6 files changed

+56
-155
lines changed

cli/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ doc = false
2020
name = "benchmark"
2121
harness = false
2222

23+
[features]
24+
wasm = ["tree-sitter/wasm", "tree-sitter-loader/wasm"]
25+
2326
[dependencies]
2427
ansi_term = "0.12.1"
2528
anyhow = "1.0.72"
@@ -49,7 +52,6 @@ which = "4.4.0"
4952
[dependencies.tree-sitter]
5053
version = "0.20.10"
5154
path = "../lib"
52-
features = ["wasm"]
5355

5456
[dependencies.tree-sitter-config]
5557
version = "0.19.0"
@@ -62,7 +64,6 @@ path = "../highlight"
6264
[dependencies.tree-sitter-loader]
6365
version = "0.20"
6466
path = "loader"
65-
features = ["wasm"]
6667

6768
[dependencies.tree-sitter-tags]
6869
version = "0.20"

cli/loader/src/lib.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -354,29 +354,23 @@ impl Loader {
354354
lib_name.push_str(".debug._");
355355
}
356356

357-
let mut library_path = self.parser_lib_path.join(lib_name);
358357
fs::create_dir_all(&self.parser_lib_path)?;
359358

359+
let mut library_path = self.parser_lib_path.join(lib_name);
360+
library_path.set_extension(DYLIB_EXTENSION);
361+
360362
let parser_path = src_path.join("parser.c");
361-
let mut scanner_path = None;
362-
let mut try_scanner_path = src_path.join("scanner.c");
363-
for extension in ["c", "cc", "cpp"] {
364-
try_scanner_path.set_extension(extension);
365-
if try_scanner_path.exists() {
366-
scanner_path = Some(try_scanner_path);
367-
break;
368-
}
369-
}
363+
let scanner_path = self.get_scanner_path(&src_path);
370364

365+
#[cfg(feature = "wasm")]
371366
if self.wasm_store.lock().unwrap().is_some() {
372367
library_path.set_extension("wasm");
373-
} else {
374-
library_path.set_extension(DYLIB_EXTENSION);
375368
}
376369

377370
let recompile = needs_recompile(&library_path, &parser_path, scanner_path.as_deref())
378371
.with_context(|| "Failed to compare source and binary timestamps")?;
379372

373+
#[cfg(feature = "wasm")]
380374
if let Some(wasm_store) = self.wasm_store.lock().unwrap().as_mut() {
381375
if recompile {
382376
self.compile_parser_to_wasm(
@@ -391,8 +385,10 @@ impl Loader {
391385
}
392386

393387
let wasm_bytes = fs::read(&library_path)?;
394-
Ok(wasm_store.load_language(name, &wasm_bytes))
395-
} else {
388+
return Ok(wasm_store.load_language(name, &wasm_bytes));
389+
}
390+
391+
{
396392
if recompile {
397393
self.compile_parser_to_dylib(
398394
header_path,
@@ -411,7 +407,7 @@ impl Loader {
411407
language_fn()
412408
};
413409
mem::forget(library);
414-
Ok(language)
410+
return Ok(language);
415411
}
416412
}
417413

@@ -528,7 +524,7 @@ impl Loader {
528524
Ok(())
529525
}
530526

531-
fn compile_parser_to_wasm(
527+
pub fn compile_parser_to_wasm(
532528
&self,
533529
language_name: &str,
534530
src_path: &Path,
@@ -874,6 +870,17 @@ impl Loader {
874870
pub fn use_wasm(&mut self, engine: tree_sitter::wasmtime::Engine) {
875871
*self.wasm_store.lock().unwrap() = Some(tree_sitter::WasmStore::new(engine))
876872
}
873+
874+
pub fn get_scanner_path(&self, src_path: &Path) -> Option<PathBuf> {
875+
let mut path = src_path.join("scanner.c");
876+
for extension in ["c", "cc", "cpp"] {
877+
path.set_extension(extension);
878+
if path.exists() {
879+
return Some(path);
880+
}
881+
}
882+
None
883+
}
877884
}
878885

879886
impl<'a> LanguageConfiguration<'a> {

cli/src/main.rs

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use glob::glob;
44
use std::collections::HashSet;
55
use std::path::{Path, PathBuf};
66
use std::{env, fs, u64};
7-
use tree_sitter::{ffi, Parser, Point, WasmStore};
7+
use tree_sitter::{ffi, Parser, Point};
88
use tree_sitter_cli::{
99
generate, highlight, logger,
1010
parse::{self, ParseFileOptions, ParseOutput},
@@ -390,8 +390,6 @@ fn run() -> Result<()> {
390390
let debug_build = matches.is_present("debug-build");
391391
let update = matches.is_present("update");
392392
let filter = matches.value_of("filter");
393-
let wasm = matches.is_present("wasm");
394-
let mut parser = Parser::new();
395393
let apply_all_captures = matches.is_present("apply-all-captures");
396394

397395
if debug {
@@ -401,10 +399,13 @@ fn run() -> Result<()> {
401399

402400
loader.use_debug_build(debug_build);
403401

404-
if wasm {
402+
let mut parser = Parser::new();
403+
404+
#[cfg(feature = "wasm")]
405+
if matches.is_present("wasm") {
405406
let engine = tree_sitter::wasmtime::Engine::default();
406407
parser
407-
.set_wasm_store(WasmStore::new(engine.clone()))
408+
.set_wasm_store(tree_sitter::WasmStore::new(engine.clone()))
408409
.unwrap();
409410
loader.use_wasm(engine);
410411
}
@@ -433,33 +434,27 @@ fn run() -> Result<()> {
433434
)?;
434435
}
435436

436-
let mut store = parser.take_wasm_store();
437-
438437
// Check that all of the queries are valid.
439438
test::check_queries_at_path(*language, &current_dir.join("queries"))?;
440439

441440
// Run the syntax highlighting tests.
442441
let test_highlight_dir = test_dir.join("highlight");
443442
if test_highlight_dir.is_dir() {
444443
let mut highlighter = Highlighter::new();
445-
if let Some(store) = store.take() {
446-
highlighter.parser().set_wasm_store(store).unwrap();
447-
}
444+
highlighter.parser = parser;
448445
test_highlight::test_highlights(
449446
&loader,
450447
&mut highlighter,
451448
&test_highlight_dir,
452449
apply_all_captures,
453450
)?;
454-
store = highlighter.parser().take_wasm_store();
451+
parser = highlighter.parser;
455452
}
456453

457454
let test_tag_dir = test_dir.join("tags");
458455
if test_tag_dir.is_dir() {
459456
let mut tags_context = TagsContext::new();
460-
if let Some(store) = store.take() {
461-
tags_context.parser().set_wasm_store(store).unwrap();
462-
}
457+
tags_context.parser = parser;
463458
test_tags::test_tags(&loader, &mut tags_context, &test_tag_dir)?;
464459
}
465460
}
@@ -490,7 +485,6 @@ fn run() -> Result<()> {
490485
})?;
491486

492487
let time = matches.is_present("time");
493-
let wasm = matches.is_present("wasm");
494488
let edits = matches
495489
.values_of("edits")
496490
.map_or(Vec::new(), |e| e.collect());
@@ -504,10 +498,11 @@ fn run() -> Result<()> {
504498

505499
loader.use_debug_build(debug_build);
506500

507-
if wasm {
501+
#[cfg(feature = "wasm")]
502+
if matches.is_present("wasm") {
508503
let engine = tree_sitter::wasmtime::Engine::default();
509504
parser
510-
.set_wasm_store(WasmStore::new(engine.clone()))
505+
.set_wasm_store(tree_sitter::WasmStore::new(engine.clone()))
511506
.unwrap();
512507
loader.use_wasm(engine);
513508
}
@@ -744,6 +739,7 @@ fn run() -> Result<()> {
744739
("build-wasm", Some(matches)) => {
745740
let grammar_path = current_dir.join(matches.value_of("path").unwrap_or(""));
746741
wasm::compile_language_to_wasm(
742+
&loader,
747743
&grammar_path,
748744
&current_dir,
749745
matches.is_present("docker"),

cli/src/wasm.rs

Lines changed: 15 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
use super::generate::parse_grammar::GrammarJSON;
2-
use anyhow::{anyhow, Context, Result};
3-
use path_slash::PathExt as _;
4-
use std::{
5-
ffi::{OsStr, OsString},
6-
fs,
7-
path::Path,
8-
process::Command,
9-
};
10-
use tree_sitter_loader::EMSCRIPTEN_TAG;
11-
use which::which;
2+
use anyhow::{Context, Result};
3+
use std::{fs, path::Path};
4+
use tree_sitter_loader::Loader;
125

136
pub fn load_language_wasm_file(language_dir: &Path) -> Result<(String, Vec<u8>)> {
147
let grammar_name = get_grammar_name(&language_dir)
@@ -35,119 +28,23 @@ pub fn get_grammar_name(language_dir: &Path) -> Result<String> {
3528
}
3629

3730
pub fn compile_language_to_wasm(
31+
loader: &Loader,
3832
language_dir: &Path,
3933
output_dir: &Path,
4034
force_docker: bool,
4135
) -> Result<()> {
4236
let grammar_name = get_grammar_name(&language_dir)?;
4337
let output_filename = output_dir.join(&format!("tree-sitter-{}.wasm", grammar_name));
44-
45-
let emcc_bin = if cfg!(windows) { "emcc.bat" } else { "emcc" };
46-
let emcc_path = which(emcc_bin)
47-
.ok()
48-
.and_then(|p| Command::new(&p).output().and(Ok(p)).ok());
49-
50-
let mut command;
51-
if !force_docker && emcc_path.is_some() {
52-
command = Command::new(emcc_path.unwrap());
53-
command.current_dir(&language_dir);
54-
} else if Command::new("docker").output().is_ok() {
55-
command = Command::new("docker");
56-
command.args(&["run", "--rm"]);
57-
58-
// Mount the parser directory as a volume
59-
let mut volume_string;
60-
if let (Some(parent), Some(filename)) = (language_dir.parent(), language_dir.file_name()) {
61-
volume_string = OsString::from(parent);
62-
volume_string.push(":/src:Z");
63-
command.arg("--workdir");
64-
command.arg(Path::new("/src").join(filename).to_slash_lossy().as_ref());
65-
} else {
66-
volume_string = OsString::from(language_dir);
67-
volume_string.push(":/src:Z");
68-
command.args(&["--workdir", "/src"]);
69-
}
70-
71-
command.args(&[OsStr::new("--volume"), &volume_string]);
72-
73-
// Get the current user id so that files created in the docker container will have
74-
// the same owner.
75-
if cfg!(unix) {
76-
let user_id_output = Command::new("id")
77-
.arg("-u")
78-
.output()
79-
.with_context(|| "Failed to get get current user id")?;
80-
let user_id = String::from_utf8_lossy(&user_id_output.stdout);
81-
let user_id = user_id.trim();
82-
command.args(&["--user", user_id]);
83-
}
84-
85-
// Run `emcc` in a container using the `emscripten-slim` image
86-
command.args(&[EMSCRIPTEN_TAG, "emcc"]);
87-
} else {
88-
if force_docker {
89-
return Err(anyhow!(
90-
"You must have docker on your PATH to run this command with --docker"
91-
));
92-
}
93-
return Err(anyhow!(
94-
"You must have either emcc or docker on your PATH to run this command"
95-
));
96-
}
97-
98-
command.arg("-o").arg(&output_filename);
99-
command.args(&[
100-
"-Os",
101-
"-s",
102-
"WASM=1",
103-
"-s",
104-
"SIDE_MODULE=1",
105-
"-s",
106-
"TOTAL_MEMORY=33554432",
107-
"-s",
108-
"NODEJS_CATCH_EXIT=0",
109-
"-s",
110-
"NODEJS_CATCH_REJECTION=0",
111-
"-s",
112-
&format!("EXPORTED_FUNCTIONS=[\"_tree_sitter_{}\"]", grammar_name),
113-
"-fno-exceptions",
114-
"-I",
115-
"src",
116-
]);
117-
118-
let src = Path::new("src");
119-
let parser_c_path = src.join("parser.c");
120-
let scanner_c_path = src.join("scanner.c");
121-
let scanner_cc_path = src.join("scanner.cc");
122-
let scanner_cpp_path = src.join("scanner.cpp");
123-
124-
if language_dir.join(&scanner_cc_path).exists() {
125-
command
126-
.arg("-xc++")
127-
.arg(scanner_cc_path.to_slash_lossy().as_ref());
128-
} else if language_dir.join(&scanner_cpp_path).exists() {
129-
command
130-
.arg("-xc++")
131-
.arg(scanner_cpp_path.to_slash_lossy().as_ref());
132-
} else if language_dir.join(&scanner_c_path).exists() {
133-
command.arg(scanner_c_path.to_slash_lossy().as_ref());
134-
}
135-
136-
command.arg(parser_c_path.to_slash_lossy().as_ref());
137-
138-
let output = command
139-
.output()
140-
.with_context(|| "Failed to run emcc command")?;
141-
if !output.status.success() {
142-
return Err(anyhow!(
143-
"emcc command failed - {}",
144-
String::from_utf8_lossy(&output.stderr)
145-
));
146-
}
147-
148-
// Move the created `.wasm` file into the current working directory.
149-
fs::rename(&language_dir.join(&output_filename), &output_filename)
150-
.with_context(|| format!("Couldn't find output file {:?}", output_filename))?;
151-
38+
let src_path = language_dir.join("src");
39+
let scanner_path = loader.get_scanner_path(&src_path);
40+
loader.compile_parser_to_wasm(
41+
&grammar_name,
42+
&src_path,
43+
scanner_path
44+
.as_ref()
45+
.and_then(|p| Some(Path::new(p.file_name()?))),
46+
&output_filename,
47+
force_docker,
48+
)?;
15249
Ok(())
15350
}

highlight/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub struct HighlightConfiguration {
127127
/// syntax highlighting calls. A separate highlighter is needed for each thread that
128128
/// is performing highlighting.
129129
pub struct Highlighter {
130-
parser: Parser,
130+
pub parser: Parser,
131131
cursors: Vec<QueryCursor>,
132132
}
133133

tags/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub struct NamedCapture {
4343
}
4444

4545
pub struct TagsContext {
46-
parser: Parser,
46+
pub parser: Parser,
4747
cursor: QueryCursor,
4848
}
4949

0 commit comments

Comments
 (0)