From b787f31481b0d3421a8d6a8ab0f2fb06702fc6d6 Mon Sep 17 00:00:00 2001 From: Antonin Delpeuch Date: Sat, 2 Aug 2025 10:57:29 +0200 Subject: [PATCH] feat(bindings): generate in 2 steps via make/cmake (#4646) This makes it possible to run `make` or `cmake --build` in grammar repos where `grammar.json` hasn't been included, and still get the parser compilation to work. --- crates/cli/src/init.rs | 40 +++++++++++++++++++++++ crates/cli/src/templates/cmakelists.cmake | 9 ++++- crates/cli/src/templates/makefile | 5 ++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/crates/cli/src/init.rs b/crates/cli/src/init.rs index ae1d3311..62c30766 100644 --- a/crates/cli/src/init.rs +++ b/crates/cli/src/init.rs @@ -487,6 +487,22 @@ pub fn generate_grammar_files( if !contents.contains("cd '$(DESTDIR)$(LIBDIR)' && ln -sf") { eprintln!("Replacing Makefile"); generate_file(path, MAKEFILE_TEMPLATE, language_name, &generate_opts)?; + } else { + let contents = contents + .replace( + indoc! {r" + $(PARSER): $(SRC_DIR)/grammar.json + $(TS) generate $^ + "}, + indoc! {r" + $(SRC_DIR)/grammar.json: grammar.js + $(TS) generate --stage=json $^ + + $(PARSER): $(SRC_DIR)/grammar.json + $(TS) generate --stage=parser $^ + "} + ); + write_file(path, contents)?; } Ok(()) }, @@ -518,6 +534,30 @@ pub fn generate_grammar_files( INTERFACE $ $) "} + ).replace( + indoc! {r#" + add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" + COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json + --abi=${TREE_SITTER_ABI_VERSION} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Generating parser.c") + "#}, + indoc! {r#" + add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/grammar.js" + COMMAND "${TREE_SITTER_CLI}" generate grammar.js + --stage=json + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Generating grammar.json") + + add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" + COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json + --stage=parser --abi=${TREE_SITTER_ABI_VERSION} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Generating parser.c") + "#} ); write_file(path, contents)?; Ok(()) diff --git a/crates/cli/src/templates/cmakelists.cmake b/crates/cli/src/templates/cmakelists.cmake index 34dd8efc..2b7a53f4 100644 --- a/crates/cli/src/templates/cmakelists.cmake +++ b/crates/cli/src/templates/cmakelists.cmake @@ -19,10 +19,17 @@ include(GNUInstallDirs) find_program(TREE_SITTER_CLI tree-sitter DOC "Tree-sitter CLI") +add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/grammar.js" + COMMAND "${TREE_SITTER_CLI}" generate grammar.js + --stage=json + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + COMMENT "Generating grammar.json") + add_custom_command(OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/src/parser.c" DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/src/grammar.json" COMMAND "${TREE_SITTER_CLI}" generate src/grammar.json - --abi=${TREE_SITTER_ABI_VERSION} + --stage=parser --abi=${TREE_SITTER_ABI_VERSION} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Generating parser.c") diff --git a/crates/cli/src/templates/makefile b/crates/cli/src/templates/makefile index 940a0ffd..535220d5 100644 --- a/crates/cli/src/templates/makefile +++ b/crates/cli/src/templates/makefile @@ -72,8 +72,11 @@ $(LANGUAGE_NAME).pc: bindings/c/$(LANGUAGE_NAME).pc.in -e 's|@PROJECT_HOMEPAGE_URL@|$(HOMEPAGE_URL)|' \ -e 's|@CMAKE_INSTALL_PREFIX@|$(PREFIX)|' $< > $@ +$(SRC_DIR)/grammar.json: grammar.js + $(TS) generate --stage=json $^ + $(PARSER): $(SRC_DIR)/grammar.json - $(TS) generate $^ + $(TS) generate --stage=parser $^ install: all install -d '$(DESTDIR)$(DATADIR)'/tree-sitter/queries/KEBAB_PARSER_NAME '$(DESTDIR)$(INCLUDEDIR)'/tree_sitter '$(DESTDIR)$(PCLIBDIR)' '$(DESTDIR)$(LIBDIR)'