Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)

nix-gitignore: init at v3.0.0 (#46112)

closes siers/nix-gitignore#6

authored by

Raitis Veinbahs and committed by zimbatm.tngl.sh d8a7a01f 1472b990

+259
+1
doc/functions.xml
··· 17 17 <xi:include href="functions/shell.xml" /> 18 18 <xi:include href="functions/dockertools.xml" /> 19 19 <xi:include href="functions/prefer-remote-fetch.xml" /> 20 + <xi:include href="functions/nix-gitignore.xml" /> 20 21 </chapter>
+78
doc/functions/nix-gitignore.xml
··· 1 + <section xmlns="http://docbook.org/ns/docbook" 2 + xmlns:xlink="http://www.w3.org/1999/xlink" 3 + xmlns:xi="http://www.w3.org/2001/XInclude" 4 + xml:id="sec-pkgs-nix-gitignore"> 5 + <title>pkgs.nix-gitignore</title> 6 + 7 + <para> 8 + <function>pkgs.nix-gitignore</function> is a function that acts similarly to 9 + <literal>builtins.filterSource</literal> but also allows filtering with the 10 + help of the gitignore format. 11 + </para> 12 + 13 + <section xml:id="sec-pkgs-nix-gitignore-usage"> 14 + <title>Usage</title> 15 + 16 + <para> 17 + <literal>pkgs.nix-gitignore</literal> exports a number of functions, but 18 + you'll most likely need either <literal>gitignoreSource</literal> or 19 + <literal>gitignoreSourcePure</literal>. As their first argument, they both 20 + accept either 1. a file with gitignore lines or 2. a string 21 + with gitignore lines, or 3. a list of either of the two. They will be 22 + concatenated into a single big string. 23 + </para> 24 + 25 + <programlisting><![CDATA[ 26 + { pkgs ? import <nixpkgs> {} }: 27 + 28 + nix-gitignore.gitignoreSource [] ./source 29 + # Simplest version 30 + 31 + nix-gitignore.gitignoreSource "supplemental-ignores\n" ./source 32 + # This one reads the ./source/.gitignore and concats the auxiliary ignores 33 + 34 + nix-gitignore.gitignoreSourcePure "ignore-this\nignore-that\n" ./source 35 + # Use this string as gitignore, don't read ./source/.gitignore. 36 + 37 + nix-gitignore.gitignoreSourcePure ["ignore-this\nignore-that\n", ~/.gitignore] ./source 38 + # It also accepts a list (of strings and paths) that will be concatenated 39 + # once the paths are turned to strings via readFile. 40 + ]]></programlisting> 41 + 42 + <para> 43 + These functions are derived from the <literal>Filter</literal> functions 44 + by setting the first filter argument to <literal>(_: _: true)</literal>: 45 + </para> 46 + 47 + <programlisting><![CDATA[ 48 + gitignoreSourcePure = gitignoreFilterSourcePure (_: _: true); 49 + gitignoreSource = gitignoreFilterSource (_: _: true); 50 + ]]></programlisting> 51 + 52 + <para> 53 + Those filter functions accept the same arguments the <literal>builtins.filterSource</literal> function would pass to its filters, thus <literal>fn: gitignoreFilterSourcePure fn ""</literal> should be extensionally equivalent to <literal>filterSource</literal>. The file is blacklisted iff it's blacklisted by either your filter or the gitignoreFilter. 54 + </para> 55 + 56 + <para> 57 + If you want to make your own filter from scratch, you may use 58 + </para> 59 + 60 + <programlisting><![CDATA[ 61 + gitignoreFilter = ign: root: filterPattern (gitignoreToPatterns ign) root; 62 + ]]></programlisting> 63 + </section> 64 + 65 + <section xml:id="sec-pkgs-nix-gitignore-usage-recursive"> 66 + <title>gitignore files in subdirectories</title> 67 + 68 + <para> 69 + If you wish to use a filter that would search for .gitignore files in subdirectories, just like git does by default, use this function: 70 + </para> 71 + 72 + <programlisting><![CDATA[ 73 + gitignoreFilterRecursiveSource = filter: patterns: root: 74 + # OR 75 + gitignoreRecursiveSource = gitignoreFilterSourcePure (_: _: true); 76 + ]]></programlisting> 77 + </section> 78 + </section>
+178
pkgs/build-support/nix-gitignore/default.nix
··· 1 + # https://github.com/siers/nix-gitignore/ 2 + 3 + { lib, runCommand }: 4 + 5 + # An interesting bit from the gitignore(5): 6 + # - A slash followed by two consecutive asterisks then a slash matches 7 + # - zero or more directories. For example, "a/**/b" matches "a/b", 8 + # - "a/x/b", "a/x/y/b" and so on. 9 + 10 + with builtins; 11 + 12 + let 13 + debug = a: trace a a; 14 + last = l: elemAt l ((length l) - 1); 15 + 16 + throwIfOldNix = let required = "2.0"; in 17 + if compareVersions nixVersion required == -1 18 + then throw "nix (v${nixVersion} =< v${required}) is too old for nix-gitignore" 19 + else true; 20 + in rec { 21 + # [["good/relative/source/file" true] ["bad.tmpfile" false]] -> root -> path 22 + filterPattern = patterns: root: 23 + (name: _type: 24 + let 25 + relPath = lib.removePrefix ((toString root) + "/") name; 26 + matches = pair: (match (head pair) relPath) != null; 27 + matched = map (pair: [(matches pair) (last pair)]) patterns; 28 + in 29 + last (last ([[true true]] ++ (filter head matched))) 30 + ); 31 + 32 + # string -> [[regex bool]] 33 + gitignoreToPatterns = gitignore: 34 + assert throwIfOldNix; 35 + let 36 + # ignore -> bool 37 + isComment = i: (match "^(#.*|$)" i) != null; 38 + 39 + # ignore -> [ignore bool] 40 + computeNegation = l: 41 + let split = match "^(!?)(.*)" l; 42 + in [(elemAt split 1) (head split == "!")]; 43 + 44 + # ignore -> regex 45 + substWildcards = 46 + let 47 + special = "^$.+{}()"; 48 + escs = "\\*?"; 49 + splitString = 50 + let recurse = str : [(substring 0 1 str)] ++ 51 + (if str == "" then [] else (recurse (substring 1 (stringLength(str)) str) )); 52 + in str : recurse str; 53 + chars = s: filter (c: c != "" && !isList c) (splitString s); 54 + escape = s: map (c: "\\" + c) (chars s); 55 + in 56 + replaceStrings 57 + ((chars special) ++ (escape escs) ++ ["**/" "**" "*" "?"]) 58 + ((escape special) ++ (escape escs) ++ ["(.*/)?" ".*" "[^/]*" "[^/]"]); 59 + 60 + # (regex -> regex) -> regex -> regex 61 + mapAroundCharclass = f: r: # rl = regex or list 62 + let slightFix = replaceStrings ["\\]"] ["]"]; 63 + in 64 + concatStringsSep "" 65 + (map (rl: if isList rl then slightFix (elemAt rl 0) else f rl) 66 + (split "(\\[([^\\\\]|\\\\.)+])" r)); 67 + 68 + # regex -> regex 69 + handleSlashPrefix = l: 70 + let 71 + split = (match "^(/?)(.*)" l); 72 + findSlash = l: if (match ".+/.+" l) != null then "" else l; 73 + hasSlash = mapAroundCharclass findSlash l != l; 74 + in 75 + (if (elemAt split 0) == "/" || hasSlash 76 + then "^" 77 + else "(^|.*/)" 78 + ) + (elemAt split 1); 79 + 80 + # regex -> regex 81 + handleSlashSuffix = l: 82 + let split = (match "^(.*)/$" l); 83 + in if split != null then (elemAt split 0) + "($|/.*)" else l; 84 + 85 + # (regex -> regex) -> [regex, bool] -> [regex, bool] 86 + mapPat = f: l: [(f (head l)) (last l)]; 87 + in 88 + map (l: # `l' for "line" 89 + mapPat (l: handleSlashSuffix (handleSlashPrefix (mapAroundCharclass substWildcards l))) 90 + (computeNegation l)) 91 + (filter (l: !isList l && !isComment l) 92 + (split "\n" gitignore)); 93 + 94 + gitignoreFilter = ign: root: filterPattern (gitignoreToPatterns ign) root; 95 + 96 + # string|[string|file] (→ [string|file] → [string]) -> string 97 + gitignoreCompileIgnore = file_str_patterns: root: 98 + let 99 + onPath = f: a: if typeOf a == "path" then f a else a; 100 + str_patterns = map (onPath readFile) (lib.toList file_str_patterns); 101 + in concatStringsSep "\n" str_patterns; 102 + 103 + gitignoreFilterPure = filter: patterns: root: name: type: 104 + gitignoreFilter (gitignoreCompileIgnore patterns root) root name type 105 + && filter name type; 106 + 107 + # This is a very hacky way of programming this! 108 + # A better way would be to reuse existing filtering by making multiple gitignore functions per each root. 109 + # Then for each file find the set of roots with gitignores (and functions). 110 + # This would make gitignoreFilterSource very different from gitignoreFilterPure. 111 + # rootPath → gitignoresConcatenated 112 + compileRecursiveGitignore = root: 113 + let 114 + dirOrIgnore = file: type: baseNameOf file == ".gitignore" || type == "directory"; 115 + ignores = builtins.filterSource dirOrIgnore root; 116 + in readFile ( 117 + runCommand "${baseNameOf root}-recursive-gitignore" {} '' 118 + cd ${ignores} 119 + 120 + find -type f -exec sh -c ' 121 + rel="$(realpath --relative-to=. "$(dirname "$1")")/" 122 + if [ "$rel" = "./" ]; then rel=""; fi 123 + 124 + awk -v prefix="$rel" -v root="$1" -v top="$(test -z "$rel" && echo 1)" " 125 + BEGIN { print \"# \"root } 126 + 127 + /^!?[^\\/]+\/?$/ { 128 + match(\$0, /^!?/, negation) 129 + sub(/^!?/, \"\") 130 + 131 + if (top) { middle = \"\" } else { middle = \"**/\" } 132 + 133 + print negation[0] prefix middle \$0 134 + } 135 + 136 + /^!?(\\/|.*\\/.+$)/ { 137 + match(\$0, /^!?/, negation) 138 + sub(/^!?/, \"\") 139 + 140 + if (!top) sub(/^\//, \"\") 141 + 142 + print negation[0] prefix \$0 143 + } 144 + 145 + END { print \"\" } 146 + " "$1" 147 + ' sh {} \; > $out 148 + ''); 149 + 150 + withGitignoreFile = patterns: root: 151 + lib.toList patterns ++ [(root + "/.gitignore")]; 152 + 153 + withRecursiveGitignoreFile = patterns: root: 154 + lib.toList patterns ++ [(compileRecursiveGitignore root)]; 155 + 156 + # filterSource derivatives 157 + 158 + gitignoreFilterSourcePure = filter: patterns: root: 159 + filterSource (gitignoreFilterPure filter patterns root) root; 160 + 161 + gitignoreFilterSource = filter: patterns: root: 162 + gitignoreFilterSourcePure filter (withGitignoreFile patterns root) root; 163 + 164 + gitignoreFilterRecursiveSource = filter: patterns: root: 165 + gitignoreFilterSourcePure filter (withRecursiveGitignoreFile patterns root) root; 166 + 167 + # "Filter"-less alternatives 168 + 169 + gitignoreSourcePure = gitignoreFilterSourcePure (_: _: true); 170 + gitignoreSource = patterns: let type = typeOf patterns; in 171 + if (type == "string" && pathExists patterns) || type == "path" 172 + then throw 173 + "type error in gitignoreSource(patterns -> source -> path), " 174 + "use [] or \"\" if there are no additional patterns" 175 + else gitignoreFilterSource (_: _: true) patterns; 176 + 177 + gitignoreRecursiveSource = gitignoreFilterSourcePure (_: _: true); 178 + }
+2
pkgs/top-level/all-packages.nix
··· 309 309 310 310 nixBufferBuilders = import ../build-support/emacs/buffer.nix { inherit (pkgs) lib writeText; inherit (emacsPackagesNg) inherit-local; }; 311 311 312 + nix-gitignore = callPackage ../build-support/nix-gitignore { }; 313 + 312 314 pathsFromGraph = ../build-support/kernel/paths-from-graph.pl; 313 315 314 316 pruneLibtoolFiles = makeSetupHook { name = "prune-libtool-files"; }