lux_lib/build/
treesitter_parser.rs1use crate::config::{Config, LuaVersionUnset};
2use crate::lua_installation::LuaInstallation;
3use crate::lua_rockspec::Build;
4use crate::lua_rockspec::{BuildInfo, TreesitterParserBuildSpec};
5use crate::progress::{Progress, ProgressBar};
6use crate::tree::{RockLayout, Tree};
7use std::collections::HashMap;
8use std::io;
9use std::num::ParseIntError;
10use std::path::{Path, PathBuf};
11use thiserror::Error;
12use tree_sitter_generate::GenerateError;
13
14use super::external_dependency::ExternalDependencyInfo;
15
16const DEFAULT_GENERATE_ABI_VERSION: usize = tree_sitter::LANGUAGE_VERSION;
17
18#[derive(Error, Debug)]
19pub enum TreesitterBuildError {
20 #[error(transparent)]
21 LuaVersionUnset(#[from] LuaVersionUnset),
22 #[error("failed to initialise the tree-sitter loader: {0}")]
23 Loader(String),
24 #[error("invalid TREE_SITTER_LANGUAGE_VERSION: {0}")]
25 ParseAbiVersion(#[from] ParseIntError),
26 #[error("error generating tree-sitter grammar: {0}")]
27 Generate(#[from] GenerateError),
28 #[error("error compiling the tree-sitter grammar: {0}")]
29 TreesitterCompileError(String),
30 #[error("error creating directory {dir}: {err}")]
31 CreateDir { dir: PathBuf, err: io::Error },
32 #[error("error writing query file: {0}")]
33 WriteQuery(io::Error),
34}
35
36impl Build for TreesitterParserBuildSpec {
37 type Err = TreesitterBuildError;
38
39 async fn run(
40 self,
41 output_paths: &RockLayout,
42 _no_install: bool,
43 _lua: &LuaInstallation,
44 _external_dependencies: &HashMap<String, ExternalDependencyInfo>,
45 _config: &Config,
46 _tree: &Tree,
47 build_dir: &Path,
48 progress: &Progress<ProgressBar>,
49 ) -> Result<BuildInfo, Self::Err> {
50 let build_dir = self
51 .location
52 .map(|dir| build_dir.join(dir))
53 .unwrap_or(build_dir.to_path_buf());
54 if self.generate {
55 progress.map(|b| b.set_message("📖 ✍Generating tree-sitter grammar..."));
56 let abi_version = match std::env::var("TREE_SITTER_LANGUAGE_VERSION") {
57 Ok(v) => v.parse()?,
58 Err(_) => DEFAULT_GENERATE_ABI_VERSION,
59 };
60 tree_sitter_generate::generate_parser_in_directory(
61 &build_dir,
62 None,
63 None,
64 abi_version,
65 None,
66 None,
67 )?;
68 }
69 progress.map(|b| b.set_message("🌳 Building tree-sitter parser..."));
70 if self.parser {
71 let parser_dir = output_paths.etc.join("parser");
72 tokio::fs::create_dir_all(&parser_dir)
73 .await
74 .map_err(|err| TreesitterBuildError::CreateDir {
75 dir: parser_dir.clone(),
76 err,
77 })?;
78 let loader = tree_sitter_loader::Loader::with_parser_lib_path(build_dir.clone());
79 let output_path =
80 parser_dir.join(format!("{}.{}", self.lang, std::env::consts::DLL_EXTENSION));
81 std::env::set_var("CROSS_RUNNER", "");
87 loader
88 .compile_parser_at_path(&build_dir, output_path, &[])
89 .map_err(|err| TreesitterBuildError::TreesitterCompileError(err.to_string()))?;
90 }
91
92 let queries_dir = output_paths.etc.join("queries");
93 if !self.queries.is_empty() {
94 tokio::fs::create_dir_all(&queries_dir)
95 .await
96 .map_err(|err| TreesitterBuildError::CreateDir {
97 dir: queries_dir.clone(),
98 err,
99 })?;
100 }
101 for (path, content) in self.queries {
102 let dest = queries_dir.join(path);
103 tokio::fs::write(&dest, content)
104 .await
105 .map_err(TreesitterBuildError::WriteQuery)?;
106 }
107
108 Ok(BuildInfo::default())
109 }
110}