123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- ;;; 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
|