Skip to content

Commit 784f174

Browse files
committed
feat(target): use --target [native, wasm, all]
1 parent 0a5102e commit 784f174

5 files changed

Lines changed: 109 additions & 29 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
- Add support for `--target`:
4+
- `native` for native shared libraries, `dylib` or `so` (default).
5+
- `wasm` for web assembly.
6+
- `all` for all of the above.
7+
38
## [1.4.0] - 2025-03-22
49

510
### Features

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ build = "build.rs"
88
description = "A downloader/builder of many tree-sitter parsers"
99
edition = "2021"
1010
name = "tsdl"
11-
version = "1.4.0" # managed by release.sh
11+
version = "1.4.0" # managed by release.sh
1212
license = "MIT"
1313

1414
[lib]

src/args.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,35 @@ impl Command {
107107
}
108108
}
109109

110+
#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Diff, PartialEq, Eq, Serialize)]
111+
#[diff(attr(
112+
#[derive(Debug, PartialEq)]
113+
))]
114+
#[serde(rename_all = "kebab-case")]
115+
pub enum Target {
116+
Native,
117+
Wasm,
118+
All,
119+
}
120+
121+
impl Default for Target {
122+
fn default() -> Self {
123+
Self::Native
124+
}
125+
}
126+
127+
impl Target {
128+
#[must_use]
129+
pub fn native(&self) -> bool {
130+
matches!(self, Self::All | Self::Native)
131+
}
132+
133+
#[must_use]
134+
pub fn wasm(&self) -> bool {
135+
matches!(self, Self::All | Self::Wasm)
136+
}
137+
}
138+
110139
#[allow(clippy::struct_excessive_bools)]
111140
#[derive(clap::Args, Clone, Debug, Deserialize, Diff, PartialEq, Eq, Serialize)]
112141
#[diff(attr(
@@ -153,6 +182,9 @@ pub struct BuildCommand {
153182
#[serde(default)]
154183
pub show_config: bool,
155184

185+
#[arg(short, long, value_enum, default_value_t = Target::default())]
186+
pub target: Target,
187+
156188
#[command(flatten)]
157189
#[serde(default)]
158190
pub tree_sitter: TreeSitter,
@@ -161,14 +193,15 @@ pub struct BuildCommand {
161193
impl Default for BuildCommand {
162194
fn default() -> Self {
163195
Self {
164-
languages: None,
165-
parsers: None,
166196
build_dir: PathBuf::from(TSDL_BUILD_DIR),
167197
fresh: TSDL_FRESH,
198+
languages: None,
168199
ncpus: num_cpus::get(),
169200
out_dir: PathBuf::from(TSDL_OUT_DIR),
201+
parsers: None,
170202
prefix: String::from(TSDL_PREFIX),
171203
show_config: TSDL_SHOW_CONFIG,
204+
target: Target::default(),
172205
tree_sitter: TreeSitter::default(),
173206
}
174207
}

src/build.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use tokio::time;
1010
use url::Url;
1111

1212
use crate::{
13-
args::{BuildCommand, ParserConfig},
13+
args::{BuildCommand, ParserConfig, Target},
1414
config,
1515
consts::TSDL_FROM,
1616
display::{Handle, Progress, ProgressState, TICK_CHARS},
@@ -65,6 +65,7 @@ fn build(command: &BuildCommand, progress: Progress) -> Result<()> {
6565
command.build_dir.clone(),
6666
command.out_dir.clone(),
6767
&command.prefix,
68+
command.target,
6869
)?;
6970
create_dir_all(&command.out_dir)
7071
.into_diagnostic()
@@ -87,6 +88,7 @@ async fn update_screen(progress: Arc<Mutex<Progress>>) {
8788
}
8889
}
8990

91+
#[allow(clippy::too_many_arguments)]
9092
fn collect_languages(
9193
ts_cli: PathBuf,
9294
progress: Arc<Mutex<Progress>>,
@@ -95,12 +97,14 @@ fn collect_languages(
9597
build_dir: PathBuf,
9698
out_dir: PathBuf,
9799
prefix: &str,
100+
target: Target,
98101
) -> Result<Vec<Language>, error::LanguageCollection> {
99102
let (res, errs) = unique_languages(
100103
ts_cli,
101104
build_dir,
102105
out_dir,
103106
prefix,
107+
target,
104108
requested_languages,
105109
defined_parsers,
106110
progress,
@@ -120,11 +124,13 @@ type Languages = (
120124
);
121125

122126
#[allow(clippy::needless_pass_by_value)]
127+
#[allow(clippy::too_many_arguments)]
123128
fn unique_languages(
124129
ts_cli: PathBuf,
125130
build_dir: PathBuf,
126131
out_dir: PathBuf,
127132
prefix: &str,
133+
target: Target,
128134
requested_languages: Option<&Vec<String>>,
129135
defined_parsers: Option<&BTreeMap<String, ParserConfig>>,
130136
progress: Arc<Mutex<Progress>>,
@@ -155,6 +161,7 @@ fn unique_languages(
155161
out_dir.canon().unwrap(),
156162
prefix.into(),
157163
repo,
164+
target,
158165
ts_cli.clone(),
159166
)
160167
})

src/parser.rs

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use tracing::warn;
1111
use url::Url;
1212

1313
use crate::{
14+
args::Target,
1415
display::{Handle, ProgressHandle},
1516
error,
1617
git::{clone_fast, Ref},
@@ -19,6 +20,7 @@ use crate::{
1920
};
2021

2122
pub const NUM_STEPS: usize = 3;
23+
const WASM_EXTENSION: &str = "wasm";
2224

2325
pub async fn build_languages(languages: Vec<Language>) -> Result<()> {
2426
let buffer = if languages.is_empty() {
@@ -57,6 +59,7 @@ pub struct Language {
5759
out_dir: PathBuf,
5860
prefix: String,
5961
repo: Url,
62+
target: Target,
6063
ts_cli: Arc<PathBuf>,
6164
}
6265

@@ -72,6 +75,7 @@ impl Language {
7275
out_dir: PathBuf,
7376
prefix: String,
7477
repo: Url,
78+
target: Target,
7579
ts_cli: Arc<PathBuf>,
7680
) -> Self {
7781
Language {
@@ -83,6 +87,7 @@ impl Language {
8387
out_dir,
8488
prefix,
8589
repo,
90+
target,
8691
ts_cli,
8792
}
8893
}
@@ -124,7 +129,24 @@ impl Language {
124129
} else {
125130
warn!("I don't know how to generate parsers when a script/cmd is specified (it's typescript's fault)");
126131
}
127-
self.build(&dir).await?;
132+
133+
if self.target.native() {
134+
self.handle.msg(format!(
135+
"Building {} native parser: {}",
136+
self.git_ref,
137+
dir.file_name().unwrap().to_str().unwrap(),
138+
));
139+
self.build(&dir, DLL_EXTENSION).await?;
140+
}
141+
142+
if self.target.wasm() {
143+
self.handle.msg(format!(
144+
"Building {} wasm parser: {}",
145+
self.git_ref,
146+
dir.file_name().unwrap().to_str().unwrap(),
147+
));
148+
self.build(&dir, WASM_EXTENSION).await?;
149+
}
128150
self.handle.msg(format!(
129151
"Copying {} parser: {}",
130152
self.git_ref,
@@ -134,13 +156,25 @@ impl Language {
134156
Ok(())
135157
}
136158

137-
async fn build(&self, dir: &Path) -> Result<()> {
159+
async fn build(&self, dir: &Path, ext: &str) -> Result<()> {
160+
let effective_name = dir
161+
.file_name()
162+
.map(|n| {
163+
n.to_string_lossy()
164+
.strip_prefix("tree-sitter-")
165+
.map_or_else(|| n.to_string_lossy().to_string(), str::to_string)
166+
})
167+
.unwrap();
138168
self.build_script
139169
.as_ref()
140170
.map_or_else(
141171
|| {
142172
let mut cmd = Command::new(&*self.ts_cli);
143173
cmd.arg("build");
174+
if ext == WASM_EXTENSION {
175+
cmd.arg("--wasm");
176+
}
177+
cmd.args(["--output", &format!("{effective_name}.{ext}")]);
144178
cmd
145179
},
146180
|script| Command::from_str(script),
@@ -197,22 +231,27 @@ impl Language {
197231
.collect()
198232
}
199233

200-
async fn copy(&self, dir: impl Into<PathBuf>) -> Result<()> {
201-
let dir = dir.into();
202-
let prefix = &self.prefix;
203-
let dll = self.find_dll_files(&dir).await?;
204-
let name = Self::extract_parser_name(&dll);
205-
let dst = self
206-
.out_dir
207-
.clone()
208-
.join(format!("{prefix}{name}.{DLL_EXTENSION}"));
234+
async fn copy(&self, dir: &Path) -> Result<()> {
235+
if self.target.native() {
236+
self.do_copy(dir, DLL_EXTENSION).await?;
237+
}
238+
if self.target.wasm() {
239+
self.do_copy(dir, WASM_EXTENSION).await?;
240+
}
241+
Ok(())
242+
}
209243

244+
async fn do_copy(&self, dir: &Path, ext: &str) -> Result<(), miette::Error> {
245+
let dll = self.find_dll_files(dir, ext).await?;
246+
let name = Self::extract_parser_name(&dll, ext);
247+
let prefix = &self.prefix;
248+
let dst = self.out_dir.clone().join(format!("{prefix}{name}.{ext}"));
210249
fs::copy(&dll, &dst)
211250
.await
212251
.into_diagnostic()
213252
.wrap_err_with(|| format!("cp {} {}", &dll.display(), dst.display()))
214-
.map_err(|err| self.create_copy_error(&dll, err.to_string()).into())
215-
.and(Ok(()))
253+
.map_err(|err| self.create_copy_error(&dll, err.to_string()))?;
254+
Ok(())
216255
}
217256

218257
async fn clone(&self) -> Result<()> {
@@ -249,37 +288,35 @@ impl Language {
249288
.and(Ok(()))
250289
}
251290

252-
async fn find_dll_files(&self, dir: &Path) -> Result<PathBuf> {
291+
async fn find_dll_files(&self, dir: &Path, ext: &str) -> Result<PathBuf> {
253292
let mut files = fs::read_dir(&dir).await.unwrap();
254293
let mut dlls = Vec::with_capacity(1);
255294
while let Ok(Some(entry)) = files.next_entry().await {
256295
let file_name = entry.file_name();
257296
let name = file_name.as_os_str().to_str().unwrap();
258-
if entry.file_type().await.unwrap().is_file()
259-
&& name.ends_with(&format!(".{DLL_EXTENSION}"))
260-
{
297+
if entry.file_type().await.unwrap().is_file() && name.ends_with(&format!(".{ext}")) {
261298
dlls.push(dir.join(name));
262299
}
263300
}
264301
// Error handling for no DLLs or too many DLLs
265302
match dlls.len() {
266303
0 => Err(self
267-
.create_copy_error(dir, format!("Couldn't find any {DLL_EXTENSION} file"))
304+
.create_copy_error(dir, format!("Couldn't find any {ext} file"))
268305
.into()),
269306
n if n > 1 => Err(self
270-
.create_copy_error(dir, format!("Found many {DLL_EXTENSION} files: {dlls:?}"))
307+
.create_copy_error(dir, format!("Found many {ext} files: {dlls:?}"))
271308
.into()),
272309
_ => Ok(dlls[0].clone()),
273310
}
274311
}
275312

276-
fn extract_parser_name(dll_path: &Path) -> String {
313+
fn extract_parser_name(dll_path: &Path, ext: &str) -> String {
277314
let mut name = dll_path
278315
.file_name()
279316
.and_then(|n| n.to_str())
280317
.map(String::from)
281318
.unwrap();
282-
if name == format!("parser.{DLL_EXTENSION}") {
319+
if name == format!("parser.{ext}") {
283320
name = dll_path
284321
.parent()
285322
.and_then(|p| p.file_name())
@@ -290,10 +327,8 @@ impl Language {
290327
if name.starts_with("libtree-sitter-") {
291328
name = name.trim_start_matches("libtree-sitter-").to_string();
292329
}
293-
if name.ends_with(&format!(".{DLL_EXTENSION}")) {
294-
name = name
295-
.trim_end_matches(&format!(".{DLL_EXTENSION}"))
296-
.to_string();
330+
if name.ends_with(&format!(".{ext}")) {
331+
name = name.trim_end_matches(&format!(".{ext}")).to_string();
297332
}
298333
name
299334
}

0 commit comments

Comments
 (0)