Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,9 @@ jobs:
with:
tool: cargo-nextest,just

- name: Install emcc
uses: mymindstorm/setup-emsdk@v14
- name: Verify emcc
run: emcc -v

- run: just test
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

- Add support for `--target`:
- `native` for native shared libraries, `dylib` or `so` (default).
- `wasm` for web assembly.
- `all` for all of the above.

## [1.4.0] - 2025-03-22

### Features
Expand Down
2 changes: 1 addition & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ fn write_tsdl_consts(meta: &serde_json::Map<String, serde_json::Value>, out_dir:
let tsdl = meta.get("tsdl").unwrap();
let tsdl_build_dir = tsdl.get("build-dir").unwrap().as_str().unwrap();
let tsdl_config_file = tsdl.get("config").unwrap().as_str().unwrap();
let tsdl_consts = Path::new(&out_dir).join("tsdl_consts.rs");
let tsdl_fresh = tsdl.get("fresh").unwrap().as_bool().unwrap();
let tsdl_from = tsdl.get("from").unwrap().as_str().unwrap();
let tsdl_out_dir = tsdl.get("out").unwrap().as_str().unwrap();
let tsdl_prefix = tsdl.get("prefix").unwrap().as_str().unwrap();
let tsdl_ref = tsdl.get("ref").unwrap().as_str().unwrap();
let tsdl_show_config = tsdl.get("show-config").unwrap().as_bool().unwrap();
let tsdl_consts = Path::new(&out_dir).join("tsdl_consts.rs");
fs::write(
tsdl_consts,
formatdoc!(
Expand Down
1 change: 1 addition & 0 deletions cliff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ commit_parsers = [
{ message = "^perf", group = "<!-- 3 -->Performance" },
{ message = "^doc", group = "<!-- 4 -->Documentation" },
{ message = "^refactor", skip = true },
{ message = "^test", skip = true },
{ message = "^style", skip = true },
{ message = "^chore", skip = true },
{ message = ".*", group = "<!-- 5 -->Other" },
Expand Down
38 changes: 36 additions & 2 deletions src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,35 @@ impl Command {
}
}

#[derive(clap::ValueEnum, Clone, Copy, Debug, Deserialize, Diff, PartialEq, Eq, Serialize)]
#[diff(attr(
#[derive(Debug, PartialEq)]
))]
#[serde(rename_all = "kebab-case")]
pub enum Target {
Native,
Wasm,
All,
}

impl Default for Target {
fn default() -> Self {
Self::Native
}
}

impl Target {
#[must_use]
pub fn native(&self) -> bool {
matches!(self, Self::All | Self::Native)
}

#[must_use]
pub fn wasm(&self) -> bool {
matches!(self, Self::All | Self::Wasm)
}
}

#[allow(clippy::struct_excessive_bools)]
#[derive(clap::Args, Clone, Debug, Deserialize, Diff, PartialEq, Eq, Serialize)]
#[diff(attr(
Expand Down Expand Up @@ -153,6 +182,10 @@ pub struct BuildCommand {
#[serde(default)]
pub show_config: bool,

/// Build target.
#[arg(short, long, value_enum, default_value_t = Target::default())]
pub target: Target,

#[command(flatten)]
#[serde(default)]
pub tree_sitter: TreeSitter,
Expand All @@ -161,14 +194,15 @@ pub struct BuildCommand {
impl Default for BuildCommand {
fn default() -> Self {
Self {
languages: None,
parsers: None,
build_dir: PathBuf::from(TSDL_BUILD_DIR),
fresh: TSDL_FRESH,
languages: None,
ncpus: num_cpus::get(),
out_dir: PathBuf::from(TSDL_OUT_DIR),
parsers: None,
prefix: String::from(TSDL_PREFIX),
show_config: TSDL_SHOW_CONFIG,
target: Target::default(),
tree_sitter: TreeSitter::default(),
}
}
Expand Down
9 changes: 8 additions & 1 deletion src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use tokio::time;
use url::Url;

use crate::{
args::{BuildCommand, ParserConfig},
args::{BuildCommand, ParserConfig, Target},
config,
consts::TSDL_FROM,
display::{Handle, Progress, ProgressState, TICK_CHARS},
Expand Down Expand Up @@ -61,6 +61,7 @@ fn build(command: &BuildCommand, progress: Progress) -> Result<()> {
command.build_dir.clone(),
command.out_dir.clone(),
&command.prefix,
command.target,
)?;
create_dir_all(&command.out_dir)
.with_context(|| format!("Creating output dir {}", &command.out_dir.display()))?;
Expand All @@ -79,6 +80,7 @@ async fn update_screen(progress: Arc<Mutex<Progress>>) {
}
}

#[allow(clippy::too_many_arguments)]
fn collect_languages(
ts_cli: PathBuf,
progress: Arc<Mutex<Progress>>,
Expand All @@ -87,12 +89,14 @@ fn collect_languages(
build_dir: PathBuf,
out_dir: PathBuf,
prefix: &str,
target: Target,
) -> Result<Vec<Language>, error::LanguageCollection> {
let (res, errs) = unique_languages(
ts_cli,
build_dir,
out_dir,
prefix,
target,
requested_languages,
defined_parsers,
progress,
Expand All @@ -112,11 +116,13 @@ type Languages = (
);

#[allow(clippy::needless_pass_by_value)]
#[allow(clippy::too_many_arguments)]
fn unique_languages(
ts_cli: PathBuf,
build_dir: PathBuf,
out_dir: PathBuf,
prefix: &str,
target: Target,
requested_languages: Option<&Vec<String>>,
defined_parsers: Option<&BTreeMap<String, ParserConfig>>,
progress: Arc<Mutex<Progress>>,
Expand Down Expand Up @@ -147,6 +153,7 @@ fn unique_languages(
out_dir.canon().unwrap(),
prefix.into(),
repo,
target,
ts_cli.clone(),
)
})
Expand Down
4 changes: 2 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub fn run(command: &ConfigCommand, config: &Path) -> Result<()> {
);
}
ConfigCommand::Default => println!("{}", toml::to_string(&BuildCommand::default())?),
};
}
Ok(())
}

Expand All @@ -44,7 +44,7 @@ pub fn current(config: &Path, command: Option<&BuildCommand>) -> Result<BuildCom
None => {
debug!("Skipping cli args + config file merger.");
}
};
}
debug!("from_both = {:?}", from_file);
// Figment is screwing with me, and it's overriding config coming
// from Env::prefixed("TSDL_").
Expand Down
81 changes: 58 additions & 23 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use tracing::warn;
use url::Url;

use crate::{
args::Target,
display::{Handle, ProgressHandle},
error,
git::{clone_fast, Ref},
Expand All @@ -19,6 +20,7 @@ use crate::{
};

pub const NUM_STEPS: usize = 3;
pub const WASM_EXTENSION: &str = "wasm";

pub async fn build_languages(languages: Vec<Language>) -> Result<()> {
let buffer = if languages.is_empty() {
Expand Down Expand Up @@ -57,6 +59,7 @@ pub struct Language {
out_dir: PathBuf,
prefix: String,
repo: Url,
target: Target,
ts_cli: Arc<PathBuf>,
}

Expand All @@ -72,6 +75,7 @@ impl Language {
out_dir: PathBuf,
prefix: String,
repo: Url,
target: Target,
ts_cli: Arc<PathBuf>,
) -> Self {
Language {
Expand All @@ -83,6 +87,7 @@ impl Language {
out_dir,
prefix,
repo,
target,
ts_cli,
}
}
Expand Down Expand Up @@ -124,7 +129,24 @@ impl Language {
} else {
warn!("I don't know how to generate parsers when a script/cmd is specified (it's typescript's fault)");
}
self.build(&dir).await?;

if self.target.native() {
self.handle.msg(format!(
"Building {} native parser: {}",
self.git_ref,
dir.file_name().unwrap().to_str().unwrap(),
));
self.build(&dir, DLL_EXTENSION).await?;
}

if self.target.wasm() {
self.handle.msg(format!(
"Building {} wasm parser: {}",
self.git_ref,
dir.file_name().unwrap().to_str().unwrap(),
));
self.build(&dir, WASM_EXTENSION).await?;
}
self.handle.msg(format!(
"Copying {} parser: {}",
self.git_ref,
Expand All @@ -134,13 +156,25 @@ impl Language {
Ok(())
}

async fn build(&self, dir: &Path) -> Result<()> {
async fn build(&self, dir: &Path, ext: &str) -> Result<()> {
let effective_name = dir
.file_name()
.map(|n| {
n.to_string_lossy()
.strip_prefix("tree-sitter-")
.map_or_else(|| n.to_string_lossy().to_string(), str::to_string)
})
.unwrap();
self.build_script
.as_ref()
.map_or_else(
|| {
let mut cmd = Command::new(&*self.ts_cli);
cmd.arg("build");
if ext == WASM_EXTENSION {
cmd.arg("--wasm");
}
cmd.args(["--output", &format!("{effective_name}.{ext}")]);
cmd
},
|script| Command::from_str(script),
Expand Down Expand Up @@ -197,16 +231,21 @@ impl Language {
.collect()
}

async fn copy(&self, dir: impl Into<PathBuf>) -> Result<()> {
let dir = dir.into();
let prefix = &self.prefix;
let dll = self.find_dll_files(&dir).await?;
let name = Self::extract_parser_name(&dll);
let dst = self
.out_dir
.clone()
.join(format!("{prefix}{name}.{DLL_EXTENSION}"));
async fn copy(&self, dir: &Path) -> Result<()> {
if self.target.native() {
self.do_copy(dir, DLL_EXTENSION).await?;
}
if self.target.wasm() {
self.do_copy(dir, WASM_EXTENSION).await?;
}
Ok(())
}

async fn do_copy(&self, dir: &Path, ext: &str) -> Result<()> {
let dll = self.find_dll_files(dir, ext).await?;
let name = Self::extract_parser_name(&dll, ext);
let prefix = &self.prefix;
let dst = self.out_dir.clone().join(format!("{prefix}{name}.{ext}"));
fs::copy(&dll, &dst)
.await
.with_context(|| format!("cp {} {}", &dll.display(), dst.display()))
Expand Down Expand Up @@ -248,37 +287,35 @@ impl Language {
.and(Ok(()))
}

async fn find_dll_files(&self, dir: &Path) -> Result<PathBuf> {
async fn find_dll_files(&self, dir: &Path, ext: &str) -> Result<PathBuf> {
let mut files = fs::read_dir(&dir).await.unwrap();
let mut dlls = Vec::with_capacity(1);
while let Ok(Some(entry)) = files.next_entry().await {
let file_name = entry.file_name();
let name = file_name.as_os_str().to_str().unwrap();
if entry.file_type().await.unwrap().is_file()
&& name.ends_with(&format!(".{DLL_EXTENSION}"))
{
if entry.file_type().await.unwrap().is_file() && name.ends_with(&format!(".{ext}")) {
dlls.push(dir.join(name));
}
}
// Error handling for no DLLs or too many DLLs
match dlls.len() {
0 => Err(self
.create_copy_error(dir, format!("Couldn't find any {DLL_EXTENSION} file"))
.create_copy_error(dir, format!("Couldn't find any {ext} file"))
.into()),
n if n > 1 => Err(self
.create_copy_error(dir, format!("Found many {DLL_EXTENSION} files: {dlls:?}"))
.create_copy_error(dir, format!("Found many {ext} files: {dlls:?}"))
.into()),
_ => Ok(dlls[0].clone()),
}
}

fn extract_parser_name(dll_path: &Path) -> String {
fn extract_parser_name(dll_path: &Path, ext: &str) -> String {
let mut name = dll_path
.file_name()
.and_then(|n| n.to_str())
.map(String::from)
.unwrap();
if name == format!("parser.{DLL_EXTENSION}") {
if name == format!("parser.{ext}") {
name = dll_path
.parent()
.and_then(|p| p.file_name())
Expand All @@ -289,10 +326,8 @@ impl Language {
if name.starts_with("libtree-sitter-") {
name = name.trim_start_matches("libtree-sitter-").to_string();
}
if name.ends_with(&format!(".{DLL_EXTENSION}")) {
name = name
.trim_end_matches(&format!(".{DLL_EXTENSION}"))
.to_string();
if name.ends_with(&format!(".{ext}")) {
name = name.trim_end_matches(&format!(".{ext}")).to_string();
}
name
}
Expand Down
2 changes: 1 addition & 1 deletion src/tree_sitter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ fn parse_refs(stdout: &str) -> HashMap<String, String> {
for line in stdout.lines() {
let ref_line = line.split('\t').map(str::trim).collect::<Vec<_>>();
let (sha1, full_ref) = (ref_line[0], ref_line[1]);
if let Some(tag) = full_ref.split('/').last() {
if let Some(tag) = full_ref.split('/').next_back() {
trace!("insert {tag} -> {sha1}");
refs.insert(tag.to_string(), sha1.to_string());
}
Expand Down
Loading