aboutsummaryrefslogtreecommitdiff
path: root/guix/build/tree-sitter-build-system.scm
diff options
context:
space:
mode:
authorPierre Langlois <pierre.langlois@gmx.com>2022-11-25 01:57:21 +0000
committerAndrew Tropin <andrew@trop.in>2023-02-12 11:32:20 +0400
commitdbd4d2d0707b486f1e2c8659e94e1d3b15e4351e (patch)
tree131835584a8cc60536cda9c657a4807c7528a097 /guix/build/tree-sitter-build-system.scm
parent4a6f64ab2afff761eb233e7ca6110d9e8acf8f32 (diff)
downloadguix-dbd4d2d0707b486f1e2c8659e94e1d3b15e4351e.tar
guix-dbd4d2d0707b486f1e2c8659e94e1d3b15e4351e.tar.gz
build-system: Add tree-sitter-build-system.
* guix/build-system/tree-sitter.scm: New module. * guix/build/tree-sitter-build-system.scm: Likewise. * Makefile.am (MODULES): Add them. * doc/guix.texi: Document it. Signed-off-by: Andrew Tropin <andrew@trop.in>
Diffstat (limited to 'guix/build/tree-sitter-build-system.scm')
-rw-r--r--guix/build/tree-sitter-build-system.scm153
1 files changed, 153 insertions, 0 deletions
diff --git a/guix/build/tree-sitter-build-system.scm b/guix/build/tree-sitter-build-system.scm
new file mode 100644
index 0000000000..4106728bdf
--- /dev/null
+++ b/guix/build/tree-sitter-build-system.scm
@@ -0,0 +1,153 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2022 Pierre Langlois <pierre.langlois@gmx.com>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (guix build tree-sitter-build-system)
+ #:use-module ((guix build node-build-system) #:prefix node:)
+ #:use-module (guix build json)
+ #:use-module (guix build utils)
+ #:use-module (ice-9 match)
+ #:use-module (ice-9 regex)
+ #:use-module (srfi srfi-1)
+ #:export (%standard-phases
+ tree-sitter-build))
+
+;; Commentary:
+;;
+;; Build procedures for tree-sitter grammar packages. This is the
+;; builder-side code, which builds on top of the node build-system.
+;;
+;; Tree-sitter grammars are written in JavaScript and compiled to a native
+;; shared object. The `tree-sitter generate' command invokes `node' in order
+;; to evaluate the grammar.js into a grammar.json file, which is then
+;; translated into C code. We then compile the C code ourselves. Packages
+;; also sometimes add extra manually written C/C++ code.
+;;
+;; In order to support grammars depending on each other, such as C and C++,
+;; JavaScript and TypeScript, this build-system installs the source of the
+;; node module in a dedicated "js" output.
+;;
+;; Code:
+
+(define* (patch-dependencies #:key inputs #:allow-other-keys)
+ "Rewrite dependencies in 'package.json'. We remove all runtime dependencies
+and replace development dependencies with tree-sitter grammar node modules."
+
+ (define (rewrite package.json)
+ (map (match-lambda
+ (("dependencies" @ . _)
+ '("dependencies" @))
+ (("devDependencies" @ . _)
+ `("devDependencies" @
+ ,@(filter-map (match-lambda
+ ((key . directory)
+ (let ((node-module
+ (string-append directory
+ "/lib/node_modules/"
+ key)))
+ (and (directory-exists? node-module)
+ `(,key . ,node-module)))))
+ (alist-delete "node" inputs))))
+ (other other))
+ package.json))
+
+ (node:with-atomic-json-file-replacement "package.json"
+ (match-lambda
+ (('@ . package.json)
+ (cons '@ (rewrite package.json))))))
+
+;; FIXME: The node build-system's configure phase does not support
+;; cross-compiling so we re-define it.
+(define* (configure #:key native-inputs inputs #:allow-other-keys)
+ (invoke (search-input-file (or native-inputs inputs) "/bin/npm")
+ "--offline" "--ignore-scripts" "install"))
+
+(define* (build #:key grammar-directories #:allow-other-keys)
+ (for-each (lambda (dir)
+ (with-directory-excursion dir
+ ;; Avoid generating binding code for other languages, we do
+ ;; not support this use-case yet and it relies on running
+ ;; `node-gyp' to build native addons.
+ (invoke "tree-sitter" "generate" "--no-bindings")))
+ grammar-directories))
+
+(define* (check #:key grammar-directories tests? #:allow-other-keys)
+ (when tests?
+ (for-each (lambda (dir)
+ (with-directory-excursion dir
+ (invoke "tree-sitter" "test")))
+ grammar-directories)))
+
+(define* (install #:key target grammar-directories outputs #:allow-other-keys)
+ (let ((lib (string-append (assoc-ref outputs "out")
+ "/lib/tree-sitter")))
+ (mkdir-p lib)
+ (define (compile-language dir)
+ (with-directory-excursion dir
+ (let ((lang (assoc-ref (call-with-input-file "src/grammar.json"
+ read-json)
+ "name"))
+ (source-file (lambda (path)
+ (if (file-exists? path)
+ path
+ #f))))
+ (apply invoke
+ `(,(if target
+ (string-append target "-g++")
+ "g++")
+ "-shared"
+ "-fPIC"
+ "-fno-exceptions"
+ "-O2"
+ "-g"
+ "-o" ,(string-append lib "/libtree-sitter-" lang ".so")
+ ;; An additional `scanner.{c,cc}' file is sometimes
+ ;; provided.
+ ,@(cond
+ ((source-file "src/scanner.c")
+ => (lambda (file) (list "-xc" "-std=c99" file)))
+ ((source-file "src/scanner.cc")
+ => (lambda (file) (list file)))
+ (else '()))
+ "-xc" "src/parser.c")))))
+ (for-each compile-language grammar-directories)))
+
+(define* (install-js #:key native-inputs inputs outputs #:allow-other-keys)
+ (invoke (search-input-file (or native-inputs inputs) "/bin/npm")
+ "--prefix" (assoc-ref outputs "js")
+ "--global"
+ "--offline"
+ "--loglevel" "info"
+ "--production"
+ ;; Skip scripts to prevent building bindings via GYP.
+ "--ignore-scripts"
+ "install" "../package.tgz"))
+
+(define %standard-phases
+ (modify-phases node:%standard-phases
+ (replace 'patch-dependencies patch-dependencies)
+ (replace 'configure configure)
+ (replace 'build build)
+ (replace 'check check)
+ (replace 'install install)
+ (add-after 'install 'install-js install-js)))
+
+(define* (tree-sitter-build #:key inputs (phases %standard-phases)
+ #:allow-other-keys #:rest args)
+ (apply node:node-build #:inputs inputs #:phases phases args))
+
+;;; tree-sitter-build-system.scm ends here