diff --git a/_service b/_service index a2e9e7e..f82805d 100644 --- a/_service +++ b/_service @@ -1,25 +1,8 @@ - - + + tree-sitter-*.tar.xz - xz true - - + diff --git a/cargo_config b/cargo_config deleted file mode 100644 index 6fb4ff4..0000000 --- a/cargo_config +++ /dev/null @@ -1,5 +0,0 @@ -[source.crates-io] -replace-with = "vendored-sources" - -[source.vendored-sources] -directory = "vendor" \ No newline at end of file diff --git a/compile-macros.sh b/compile-macros.sh new file mode 100644 index 0000000..75bacb8 --- /dev/null +++ b/compile-macros.sh @@ -0,0 +1,64 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-2.0 +# SPDX-FileCopyrightText: 2024 Björn Bidar +# based of compile-macros.sh from python-rpm-macros +mkdir -p macros + +### Lua: generate automagic from macros.in and macros.lua +( + # copy macros.in up to LUA-MACROS + sed -n -e '1,/^### LUA-MACROS ###$/p' macros.in + + # include "functions.lua", without empty lines, as %_treesitter_definitions + echo "%_treesitter_definitions %{lua:" + sed -n -r \ + -e 's/\\/\\\\/g' \ + -e '/^.+$/p' \ + functions.lua + echo "}" + + INFUNC=0 + INMULTILINE_MACRO=0 + # brute line-by-line read of macros.lua + IFS="" + while read -r line; do + if [ $INFUNC = 0 ] ; then + if [ $INMULTILINE_MACRO = 1 ] ;then + if echo "$line" | grep -qE '^.*\]\]' ; then + INMULTILINE_MACRO=0 + fi + echo "# $line" + elif echo "$line" | grep -qE -- '--\[\[' ; then + INMULTILINE_MACRO=1 + echo "# $line" + elif echo "$line" | grep -qE -- '^--' ; then + echo "# $line" + elif echo "$line" | grep -q '^function '; then + # entering top-level Lua function + INFUNC=1; + echo "$line" | sed -r -e 's/^function (.*)\((.*)\)$/%\1(\2) %{lua: \\/' + else + # outside function, copy + # (usually this is newline) + echo "$line" + fi + else + if [ "$line" = "end" ]; then + # leaving top-level Lua function + INFUNC=0; + echo '}' + elif [ $INFUNC = 1 ]; then + # inside function + # double backslashes and add backslash to end of line + echo "$line" | sed -e 's/\\/\\\\/g' -e 's/$/\\/' + fi + fi + done < macros.lua + + # copy rest of macros.in + sed -n -e '/^### LUA-MACROS ###$/,$p' macros.in +) > macros/050-automagic + + +### final step: cat macros/*, but with files separated by additional newlines +sed -e '$s/$/\n/' -s macros/* > macros.treesitter diff --git a/functions.lua b/functions.lua new file mode 100644 index 0000000..41eaa0b --- /dev/null +++ b/functions.lua @@ -0,0 +1,45 @@ +--[[ + SPDX-License-Identifier: GPL-2.0 + SPDX-FileCopyrightText: 2024 Björn Bidar + + partly based of functions.lua from python-rpm-macros +--]] + +-- declare common functions +function string.startswith(str, prefix) + return str:sub(1, prefix:len()) == prefix +end + +function string.endswith(str, suffix) + return str:sub(-suffix:len()) == suffix +end + +function string.dirname(str) + if str:match("(.*)/") == "" then + return nil + else + return str:match("(.*)/") + end +end + +function string.basename(str) + while true do + local idx = str:find("/") + if not idx then return str end + str = str:sub(idx + 1) + end +end + +function string.split(str, sep) + if sep == nil then + sep = '%s' + end + + local res = {} + local func = function(w) + table.insert(res, w) + end + + string.gsub(str, '[^'..sep..']+', func) + return res +end diff --git a/macros.in b/macros.in new file mode 100644 index 0000000..d1e5a91 --- /dev/null +++ b/macros.in @@ -0,0 +1,26 @@ +# -*- rpm-spec -*- +# SPDX-License-Identifier: GPL-2.0 +# SPDX-FileCopyrightText: 2024 Björn Bidar +%_treesitter_base_name tree-sitter +%_treesitter_grammardir %{_libdir} +%_treesitter_grammar_develdir %{_includedir}/%{_treesitter_base_name}/grammars +%_treesitter_grammar_base_libname lib%{_treesitter_base_name} +%treesitter_target() %{_rpmconfigdir}/tree-sitter-target.py +%treesitter_set_flags export NODE_PATH=$NODE_PATH:%{_treesitter_grammar_develdir}:$PWD + +%__treesitter_devel_package_name() %name-devel +%treesitter_devel_package \ +%package -n %{__treesitter_devel_package_name} \ +Summary: Devel package for %{name} containing it's grammar source \ +BuildArch: noarch \ +%{_treesitter_devel_provides} \ +%description -n %{__treesitter_devel_package_name} \ +This package contains grammar sources for use in other grammars. \ +%files -n %{__treesitter_devel_package_name} \ +%{treesitter_devel_files} + +### LUA-MACROS ### + + + +%_treesitter_macro_init %{_treesitter_definitions}%{lua: rpm.define("_treesitter_macro_init %{nil}")} diff --git a/macros.lua b/macros.lua new file mode 100644 index 0000000..dc0fd5d --- /dev/null +++ b/macros.lua @@ -0,0 +1,211 @@ +--[[ + SPDX-License-Identifier: GPL-2.0 + SPDX-FileCopyrightText: 2024 Björn Bidar + partly based of functions.lua from python-rpm-macros +--]] + + +--[[ + Main Package should look like this: + %%treesitter_grammars foo bar + + %%build + %%treesitter_configure + %%treesitter_build + + %%install + %%treesitter_install + + %%files + %%treesitter_files +--]] + + +function treesitter_grammars() + --[[ + Define any grammars to be included inside the package + --]] + rpm.expand("%_treesitter_macro_init") + local base_name = rpm.expand("%_treesitter_base_name") + local base_libname = rpm.expand("%_treesitter_grammar_base_libname") + + local treesitter_grammar_names = "" + local treesitter_grammar_libnames = "" + + for arg_num = 1,#arg do + treesitter_grammar_libnames=treesitter_grammar_libnames .. base_libname .. "-" .. arg[arg_num] .. ".so " + end + rpm.define("treesitter_grammar_libnames " .. treesitter_grammar_libnames) + + for arg_num = 1,#arg do + treesitter_grammar_names=treesitter_grammar_names .. " " .. arg[arg_num] + print("Provides: treesitter_grammar(" .. base_name .. "-" .. arg[arg_num] .. ")\n") + end + rpm.define("treesitter_grammar_names " .. treesitter_grammar_names) + +end + + +function treesitter_configure() + --[[ + Generate grammar sources for all the grammars provided earlier akin + to %configure. + --]] + rpm.expand("%_treesitter_macro_init") + local grammars = string.split(rpm.expand("%{treesitter_grammar_names}")) + + print(rpm.expand("%treesitter_set_flags")) + print("\n") + + if #grammars > 1 then + for k,grammar in pairs(grammars) do + print("(cd " .. grammar .. ";tree-sitter generate)") + print("\n") + end + else + print("tree-sitter generate") + end + +end + +function treesitter_build() + --[[ + Similar to %make_build build all grammars if possible read from + an alternative file instead of binding.gyp + --]] + rpm.expand("%_treesitter_macro_init") + local basename = rpm.expand("%{_treesitter_grammar_base_libname}") + local grammar_names = rpm.expand("%treesitter_grammar_names") + local left_over_args = arg[1] + local grammar_arg_binding = "" + + + if left_over_args then + grammar_arg_binding=" -b "..arg[1] + end + + local treesitter_target = rpm.expand("%{treesitter_target}") + local grammar_names_tbl = string.split(grammar_names, " ") + + if #grammar_names_tbl > 1 then + for k,target in pairs(grammar_names_tbl) do + print("eval $(" .. treesitter_target .. grammar_arg_binding .. " -g " .. target ..") " .. " -o " .. basename .. "-" .. target .. ".so ${RPM_OPT_FLAGS}") + print("\n") + end + else + print("eval $(" .. treesitter_target .. grammar_arg_binding .. ") " .. " -o " .. basename.. "-" .. grammar_names .. ".so ${RPM_OPT_FLAGS}") + end +end + + + +function treesitter_install() + --[[ + Install all previously build grammars + --]] + rpm.expand("%_treesitter_macro_init") + local grammars = string.split(rpm.expand("%{treesitter_grammar_libnames}")) + local install_path = rpm.expand("%{buildroot}%{_treesitter_grammardir}") + for k,grammar in pairs(grammars) do + print("install -Dm755 " .. grammar .. " " .. install_path .. "/" .. grammar) + print("\n") + end +end + +function treesitter_files() + rpm.expand("%_treesitter_macro_init") + local grammars = string.split(rpm.expand("%{treesitter_grammar_libnames}")) + local grammardir = rpm.expand("%{_treesitter_grammardir}") + local _libdir = rpm.expand("%{_libdir}") + + if not grammardir == libdir then + print(rpm.expand("%dir " .. grammardir.."\n")) + end + + for k,grammar in pairs(grammars) do + print(rpm.expand(grammardir .. "/"..grammar.."\n")) + end +end + +--[[ + Optional -devel package for grammars that are needed for other grammars to be built. + + If the -devel package is needed it should look like this: + %%install + [...] # Main page here + + %%treesitter_devel_install + + # Or if the package has shared files between grammars: + %%treesitter_devel_install foobar.js + + %%treesitter_devel_package +--]] + +function treesitter_devel_install() + --[[ + Install all grammars sources defined earlier. + If passed these can also include additional files such as shared fragments + that are used between multiple grammars in the same package. + --]] + rpm.expand("%_treesitter_macro_init") + --local grammar_names = rpm.expand("%{treesitter_grammar_names}") + local grammars = string.split(rpm.expand("%{treesitter_grammar_names}")) + local treesitter = rpm.expand("%_treesitter_base_name") + + local install_cmd_base = "install -Dm644 " + local install_path = rpm.expand("%{buildroot}%{_treesitter_grammar_develdir}/") + + local rpm_provides_macro = "" + --print(grammar_names) + for k,grammar in pairs(grammars) do + if grammar then + rpm_provides_macro=rpm_provides_macro.. "Provides: treesitter_grammar_src(" ..treesitter .. "-" .. grammar ..")\n" + end + end + rpm.define("_treesitter_devel_provides "..rpm_provides_macro) + + if #grammars == 1 then + print(install_cmd_base .. "grammar.js " .. install_path .. treesitter .. "-" .. grammars[1].. "/grammar.js") + return + end + + if #arg > 0 then + --[[ FIXME: This maybe not be the best solution if packages can have a single grammar + but also addon files + ]]-- + for arg_num = 1,#arg do + print(rpm.expand(install_cmd_base .. arg[arg_num] .. " " .. install_path .. "%{name}/" .. arg[arg_num] .. "\n")) + print("\n") + end + end + + for k,grammar in pairs(grammars) do + if grammar then + print(rpm.expand(install_cmd_base .. grammar .. "/grammar.js " .. install_path .. "%{name}/" .. grammar .. "/grammar.js\n")) + print("\n") + end + end +end + + +function treesitter_devel_files() + --[[ + Install -devel files to %%{_treesitter_grammar_develdir} + --]] + rpm.expand("%_treesitter_macro_init") + local grammars = string.split(rpm.expand("%{treesitter_grammar_names}")) + local grammar_develdir = rpm.expand("%{_treesitter_grammar_develdir}") + local fpp + + print(rpm.expand("%dir %{_treesitter_grammar_develdir} \n")) + --[[ + Own all directories leading up to %%_includedir which we include in + %%_treesitter_grammar_develdir + --]] + while not (grammar_develdir == rpm.expand("%_includedir")) do + print(rpm.expand("%dir " .. grammar_develdir .. "\n")) + grammar_develdir = grammar_develdir:dirname() + end + print(rpm.expand("%{_treesitter_grammar_develdir}/%{name}\n")) +end diff --git a/tree-sitter-0.20.8.tar.xz b/tree-sitter-0.20.8.tar.xz deleted file mode 100644 index 1dbf802..0000000 --- a/tree-sitter-0.20.8.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6181ede0b7470bfca37e293e7d5dc1d16469b9485d13f13a605baec4a8b1f791 -size 2941223 diff --git a/tree-sitter-0.22.2.tar.xz b/tree-sitter-0.22.2.tar.xz new file mode 100644 index 0000000..b12fbd8 --- /dev/null +++ b/tree-sitter-0.22.2.tar.xz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c829523b876d4a37e1bd46a655c133a93669c0fe98fcd84972b168849c27afc +size 3040339 diff --git a/tree-sitter-target.py b/tree-sitter-target.py new file mode 100644 index 0000000..ed9b29a --- /dev/null +++ b/tree-sitter-target.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0 +# SPDX-FileCopyrightText: 2024 Björn Bidar + +"""Generate compile commands by reading binding.gyp""" + +# pylint: disable=invalid-name +# pylint: disable=too-many-branches + +import argparse +from pathlib import Path +from typing import List, Dict, Optional +import ast +from copy import copy + +parser = argparse.ArgumentParser(prog = Path(__file__).name, + description = "Generate compile commands by reading binding.gyp") +parser.add_argument('-b', '--binding', dest = "binding", + action="store", help="Path to binding file") +parser.add_argument('-g', '--grammar', dest = "grammars", + action= "append", + required = False, + help="Specify grammars in case binding file contains more than one grammar") + +args = parser.parse_args() + +if args.binding: + binding_gyp = Path(args.binding) +else: + binding_gyp = Path("binding.gyp") + +if not binding_gyp.exists(): + raise FileNotFoundError(f"bindings {binding_gyp.absolute()} not found") + + +with open(binding_gyp, 'r', encoding='utf8') as binding_raw: + binding = ast.literal_eval(binding_raw.read()) + +targets = binding['targets'][0] + +def buildCompileCommand(target: Dict, grammars: Optional[List[str]] = None) -> Dict[ + str, + List +]: + """Generate compile commands from TARGET supplied found in GRAMMARS or src""" + cc = 'cc' + cflags_c = [] + cflags_cc = [] + commands = {} + base_command = [ cc, '-shared', '-fPIC'] + suffixes_cc = ['cc', 'cxx', 'cpp'] + if 'cflags_c' in target: + cflags_c = target['cflags_c'] + if 'clfags_cc' in target: + cflags_cc = target['cflags_cc'] + + include_dirs = [] + for include_dir in target['include_dirs']: + # Don't include any node commands + if not include_dir.startswith(' + +- Add packaging macros for tree-sitter grammar +- Add missing dependency for tree-sitter generate + +------------------------------------------------------------------- +Tue Mar 19 07:17:25 UTC 2024 - Soc Virnyl Estela + +- Update to version 0.22.2: + * fix(lib): allow hiding symbols + * feat(lib): implement Display for Node + * test: fix header writes + * chore: turbofish styling + * feat(cli)!: add a separate build command to compile parsers + * ci: simplify workflows + * docs(license): update year + * fix(lib): avoid possible UB of calling memset on a null ptr when 0 is passed into `array_grow_by` + * fix(lib): makefile installation +- Update _service file + * replace obsoleted mode "disabled" with "manual" + * use download_files instead of performing scm + ------------------------------------------------------------------- Thu Apr 6 19:36:21 UTC 2023 - Andreas Schneider diff --git a/tree-sitter.spec b/tree-sitter.spec index c5d1493..ad66f68 100644 --- a/tree-sitter.spec +++ b/tree-sitter.spec @@ -1,7 +1,7 @@ # # spec file for package tree-sitter # -# Copyright (c) 2023 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,18 +19,25 @@ %define somajor 0 %define libdirname tree_sitter Name: tree-sitter -Version: 0.20.8 +Version: 0.22.2 Release: 0 Summary: An incremental parsing system for programming tools -License: MIT +License: GPL-2.0-only AND MIT URL: https://tree-sitter.github.io/ Source0: https://github.com/tree-sitter/%{name}/archive/v%{version}.tar.gz#/%{name}-%{version}.tar.xz -Source1: vendor.tar.xz -Source10: cargo_config +Source1: vendor.tar.zst Source11: baselibs.conf +Source20: tree-sitter-target.py +Source21: macros.in +Source22: macros.lua +Source23: functions.lua +Source24: compile-macros.sh +Source25: treesitter_grammar.attr +Source26: treesitter_grammar.req BuildRequires: cargo-packaging BuildRequires: rust > 1.40 Requires: lib%{name}%{somajor} = %{version} +Requires: nodejs %{?suse_build_hwcaps_libs} %description @@ -66,8 +73,9 @@ developing applications that use %{name}. %prep %autosetup -p1 -a1 -mkdir -p .cargo -cp %{SOURCE10} .cargo/config +cp %{SOURCE21} . +cp %{SOURCE22} . +cp %{SOURCE23} . # fix VERSION in Makefile sed -i -e '/^VERSION/s/:= .*$/:= %{version}/' Makefile @@ -78,6 +86,8 @@ export CFLAGS='%{optflags}' export PREFIX='%{_prefix}' LIBDIR='%{_libdir}' %make_build +sh %{SOURCE24} + %install export PREFIX='%{_prefix}' LIBDIR='%{_libdir}' INCLUDEDIR='%{_includedir}' %make_install @@ -86,12 +96,22 @@ install -p -m 0755 -D %{_builddir}/%{name}-%{version}/target/release/tree-sitter find %{buildroot} -type f \( -name "*.la" -o -name "*.a" \) -delete -print +install -Dm644 macros.treesitter %{buildroot}%{_rpmmacrodir}/macros.treesitter +install -Dm755 %{SOURCE20} %{buildroot}%{_rpmconfigdir}/$(basename %{SOURCE20}) + +install -Dm644 %{SOURCE25} %{buildroot}%{_fileattrsdir}/$(basename %{SOURCE25}) +install -Dm755 %{SOURCE26} %{buildroot}%{_rpmconfigdir}/$(basename %{SOURCE26}) + %post -n lib%{name}%{somajor} -p /sbin/ldconfig %postun -n lib%{name}%{somajor} -p /sbin/ldconfig %files %doc README.md CONTRIBUTING.md %{_bindir}/tree-sitter +%{_rpmconfigdir}/tree-sitter-target.py +%{_rpmmacrodir}/macros.treesitter +%{_rpmconfigdir}/treesitter_grammar.req +%{_fileattrsdir}/treesitter_grammar.attr %files -n lib%{name}%{somajor} %license LICENSE diff --git a/treesitter_grammar.attr b/treesitter_grammar.attr new file mode 100644 index 0000000..2db61c7 --- /dev/null +++ b/treesitter_grammar.attr @@ -0,0 +1,2 @@ +%__treesitter_grammar_requires %{_rpmconfigdir}/treesitter_grammar.req +%__treesitter_grammar_path ^%{_treesitter_grammar_develdir}/.*\.js diff --git a/treesitter_grammar.req b/treesitter_grammar.req new file mode 100644 index 0000000..910ec77 --- /dev/null +++ b/treesitter_grammar.req @@ -0,0 +1,80 @@ +#!/usr/bin/python3 +"""Scan grammar JavaScript sources for their dependencies""" + +# SPDX-License-Identifier: MIT +# SPDX-FileCopyrightText: 2024 Björn Bidar + +# pylint: disable=invalid-name + + +import fileinput +import re +from typing import Optional +from pathlib import Path + + +treeSitterGrammarSrcPath = "/usr/include/tree_sitter" +treeSitterGrammarSymbolToken = "treesitter_grammar_src" +grammarPaths = [] + +def resolveFile(grammarFile: str) -> str: + """Resolve GRAMMARFILE from grammarPaths return found file""" + fullGrammarFilePath = Path(grammarFile) + currentGrammarPath = fullGrammarFilePath.parent + currentGrammarFile = Path(grammarFile) + if currentGrammarPath != Path('.') and currentGrammarPath.parts[0] == "..": + # resolve path relative to file found last + currentGrammarPath = grammarPaths[-1] / fullGrammarFilePath + if not currentGrammarPath.exists(): + return False + return currentGrammarPath + if currentGrammarPath.is_absolute(): + grammarPaths.append(currentGrammarPath) + if Path(grammarFile).exists(): + return fullGrammarFilePath + for path in grammarPaths: + maybeFound = path / currentGrammarFile + if maybeFound.exists(): + return maybeFound.exists() + return False + +def dummyRequire(requiredFile_fd: str, maxDepth: Optional[int] = 5 ) -> str: + """Dummy version of node's require function that spits out dependency symbols""" + if maxDepth <= 0: + return + + if not requiredFile_fd.endswith(".js"): + # Append .js to required file name in case is not specified + # we have to remove .js suffix later in any case. + requiredFile_fd+=".js" + + resolvedFile_fd = resolveFile(requiredFile_fd) + if resolvedFile_fd: + try: + with open(resolvedFile_fd, mode='r', encoding='utf8') as requiredFile: + for r_line in requiredFile: + require_re = r'.*require\((.*)\).*' + requiredLvl2 = re.match(require_re, r_line) + #print(r_line) + if requiredLvl2 is not None: + requiredLvl2_grp_cleaned = \ + requiredLvl2.group(1).removeprefix("'").removesuffix("'") + requiredLvl2_grp_cleaned = \ + requiredLvl2_grp_cleaned.removeprefix("\"").removesuffix("\"") + if not requiredLvl2_grp_cleaned.split('/')[0] == "..": + # Don't emit dependencies which are local + pass + dummyRequire(requiredLvl2_grp_cleaned, maxDepth - 1) + except FileNotFoundError: + pass + else: + if maxDepth == 5: + # In case we immediately fail to open the first grammar source file + return + # We only want to resolve dependencies outside of the package + print(f"{treeSitterGrammarSymbolToken}({Path(requiredFile_fd).parent})") + +for line in fileinput.input(): + line = line.rstrip('\n') + if line.endswith('.js'): + dummyRequire(line) diff --git a/vendor.tar.xz b/vendor.tar.xz deleted file mode 100644 index d3d47ba..0000000 --- a/vendor.tar.xz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e8ce5ccc428ccbdd44a3c3de0dd506dc5d9079fe90347fac5d5c7f885f87ad08 -size 16831820 diff --git a/vendor.tar.zst b/vendor.tar.zst new file mode 100644 index 0000000..04dbb9d --- /dev/null +++ b/vendor.tar.zst @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8158ba3e18b25a81d41e6937d120597630a57181996355154197e6988d632030 +size 30391447