Function: treesit--install-language-grammar-1

treesit--install-language-grammar-1 is a byte-compiled function defined in treesit.el.gz.

Signature

(treesit--install-language-grammar-1 OUT-DIR LANG URL &optional REVISION SOURCE-DIR CC C++)

Documentation

Install and compile a tree-sitter language grammar library.

OUT-DIR is the directory to put the compiled library file. If it is nil, the "tree-sitter" directory under user's Emacs configuration directory is used (and automatically created if it does not exist).

For LANG, URL, REVISION, SOURCE-DIR, GRAMMAR-DIR, CC, C++, see treesit-language-source-alist. If anything goes wrong, this function signals an error.

Source Code

;; Defined in /usr/src/emacs/lisp/treesit.el.gz
(defun treesit--install-language-grammar-1
    (out-dir lang url &optional revision source-dir cc c++)
  "Install and compile a tree-sitter language grammar library.

OUT-DIR is the directory to put the compiled library file.  If it
is nil, the \"tree-sitter\" directory under user's Emacs
configuration directory is used (and automatically created if it
does not exist).

For LANG, URL, REVISION, SOURCE-DIR, GRAMMAR-DIR, CC, C++, see
`treesit-language-source-alist'.  If anything goes wrong, this
function signals an error."
  (let* ((lang (symbol-name lang))
         (default-directory (make-temp-file "treesit-workdir" t))
         (workdir (expand-file-name "repo"))
         (source-dir (expand-file-name (or source-dir "src") workdir))
         (cc (or cc (seq-find #'executable-find '("cc" "gcc" "c99"))
                 ;; If no C compiler found, just use cc and let
                 ;; `call-process' signal the error.
                 "cc"))
         (c++ (or c++ (seq-find #'executable-find '("c++" "g++"))
                  "c++"))
         (soext (or (car dynamic-library-suffixes)
                    (signal 'treesit-error '("Emacs cannot figure out the file extension for dynamic libraries for this system, because `dynamic-library-suffixes' is nil"))))
         (out-dir (or (and out-dir (expand-file-name out-dir))
                      (locate-user-emacs-file "tree-sitter")))
         (lib-name (concat "libtree-sitter-" lang soext)))
    (unwind-protect
        (with-temp-buffer
          (message "Cloning repository")
          ;; git clone xxx --depth 1 --quiet [-b yyy] workdir
          (if revision
              (treesit--call-process-signal
               "git" nil t nil "clone" url "--depth" "1" "--quiet"
               "-b" revision workdir)
            (treesit--call-process-signal
             "git" nil t nil "clone" url "--depth" "1" "--quiet"
             workdir))
          ;; We need to go into the source directory because some
          ;; header files use relative path (#include "../xxx").
          ;; cd "${sourcedir}"
          (setq default-directory source-dir)
          (message "Compiling library")
          ;; cc -fPIC -c -I. parser.c
          (treesit--call-process-signal
           cc nil t nil "-fPIC" "-c" "-I." "parser.c")
          ;; cc -fPIC -c -I. scanner.c
          (when (file-exists-p "scanner.c")
            (treesit--call-process-signal
             cc nil t nil "-fPIC" "-c" "-I." "scanner.c"))
          ;; c++ -fPIC -I. -c scanner.cc
          (when (file-exists-p "scanner.cc")
            (treesit--call-process-signal
             c++ nil t nil "-fPIC" "-c" "-I." "scanner.cc"))
          ;; cc/c++ -fPIC -shared *.o -o "libtree-sitter-${lang}.${soext}"
          (apply #'treesit--call-process-signal
                 (if (file-exists-p "scanner.cc") c++ cc)
                 nil t nil
                 `("-fPIC" "-shared"
                   ,@(directory-files
                      default-directory nil
                      (rx bos (+ anychar) ".o" eos))
                   "-o" ,lib-name))
          ;; Copy out.
          (unless (file-exists-p out-dir)
            (make-directory out-dir t))
          (let* ((library-fname (expand-file-name lib-name out-dir))
                 (old-fname (concat library-fname ".old")))
            ;; Rename the existing shared library, if any, then
            ;; install the new one, and try deleting the old one.
            ;; This is for Windows systems, where we cannot simply
            ;; overwrite a DLL that is being used.
            (if (file-exists-p library-fname)
                (rename-file library-fname old-fname t))
            (copy-file lib-name (file-name-as-directory out-dir) t t)
            ;; Ignore errors, in case the old version is still used.
            (ignore-errors (delete-file old-fname)))
          (message "Library installed to %s/%s" out-dir lib-name))
      (when (file-exists-p workdir)
        (delete-directory workdir t)))))