nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 stdenv,
3 nodejs,
4 tree-sitter,
5 jq,
6 lib,
7}:
8
9{
10 language,
11 version,
12 src,
13 meta ? { },
14 generate ? false,
15 ...
16}@args:
17
18stdenv.mkDerivation (
19 {
20 pname = "tree-sitter-${language}";
21
22 inherit version src;
23
24 nativeBuildInputs = [
25 jq
26 ]
27 ++ lib.optionals generate [
28 nodejs
29 tree-sitter
30 ];
31
32 CFLAGS = [
33 "-Isrc"
34 "-O2"
35 ];
36 CXXFLAGS = [
37 "-Isrc"
38 "-O2"
39 ];
40
41 stripDebugList = [ "parser" ];
42
43 # Tree-sitter grammar packages contain a `tree-sitter.json` file at their
44 # root. This provides package metadata that can be used to infer build
45 # details.
46 #
47 # See https://tree-sitter.github.io/tree-sitter/cli/init.html for spec.
48 configurePhase = ''
49 runHook preConfigure
50 if [[ -e tree-sitter.json ]]; then
51 # Check nix package version matches grammar source
52 NIX_VERSION=${lib.head (lib.splitString "+" version)}
53 SRC_VERSION=$(jq -r '.metadata.version' tree-sitter.json)
54 if [[ "$NIX_VERSION" != "$SRC_VERSION" ]]; then
55 nixErrorLog "grammar version ($NIX_VERSION) differs from source ($SRC_VERSION)"
56 fi
57
58 # Check language name matches source
59 GRAMMAR=$(jq -c 'first(.grammars[] | select(.name == env.language))' tree-sitter.json)
60 if [[ -z "$GRAMMAR" ]]; then
61 GRAMMAR=$(jq -c 'first(.grammars[]) // {}' tree-sitter.json)
62 NAME=$(jq -r '.name' <<< "$GRAMMAR")
63 SRC_LANGS=$(jq -r '[.grammars[].name] | join(", ")' tree-sitter.json)
64 nixErrorLog "grammar name ($language) not found in source grammars ($SRC_LANGS), continuing with $NAME"
65 fi
66
67 # Move to the appropriate working directory for build
68 cd -- $(jq -r '.path // "."' <<< $GRAMMAR)
69 else
70 # Older grammars may not contain this file. The tree-sitter CLI provides
71 # a warning rather than fail unless ABI > 14. Mirror that behaviour
72 # while older grammars age out.
73 nixWarnLog "grammar source is missing tree-sitter.json"
74 fi
75 runHook postConfigure
76 '';
77
78 # Optionally regenerate the parser source from the defined grammar. In most
79 # cases this should not be required as convention is to have this checked
80 # in to the source repo.
81 preBuild = lib.optionalString generate ''
82 tree-sitter generate
83 '';
84
85 # When both scanner.{c,cc} exist, we should not link both since they may be the same but in
86 # different languages. Just randomly prefer C++ if that happens.
87 buildPhase = ''
88 runHook preBuild
89 if [[ -e src/scanner.cc ]]; then
90 $CXX -fPIC -c src/scanner.cc -o scanner.o $CXXFLAGS
91 elif [[ -e src/scanner.c ]]; then
92 $CC -fPIC -c src/scanner.c -o scanner.o $CFLAGS
93 fi
94 $CC -fPIC -c src/parser.c -o parser.o $CFLAGS
95 rm -rf parser
96 $CXX -shared -o parser *.o
97 runHook postBuild
98 '';
99
100 installPhase = ''
101 runHook preInstall
102 mkdir $out
103 mv parser $out/
104 if [[ -d queries ]]; then
105 cp -r queries $out
106 fi
107 runHook postInstall
108 '';
109
110 # Merge default meta attrs with any explicitly defined on the source.
111 meta = {
112 description = "Tree-sitter grammar for ${language}";
113 }
114 // (lib.optionalAttrs (src ? meta.homepage) {
115 homepage = src.meta.homepage;
116 })
117 // meta;
118 }
119 # FIXME: neovim and nvim-treesitter currently rely on passing location rather
120 # than a src attribute with a correctly positioned root (e.g. for grammars in
121 # monorepos). Use this if present for now.
122 // (lib.optionalAttrs (args ? location && args.location != null) {
123 setSourceRoot = "sourceRoot=$(echo */${args.location})";
124 })
125 // removeAttrs args [
126 "generate"
127 "meta"
128 ]
129)