├── README.md ├── example ├── README.md ├── flake.lock ├── flake.nix ├── folder │ ├── .gitkeep │ ├── not-me │ ├── other.not-me │ └── other │ │ └── file ├── foo1 │ └── dummy.nix └── foo10 │ └── diskoConfigurations.nix ├── flake.lock └── flake.nix /README.md: -------------------------------------------------------------------------------- 1 | 6 | # Usage 7 | 8 | ```nix 9 | # flake.nix 10 | { 11 | inputs.incl.url = "github:divnix/incl"; 12 | inputs.incl.inputs.nixpkgs.follows = "nixpkgs"; 13 | 14 | outputs = { incl, self }: { 15 | filteredSource = incl self [ 16 | ./README.md 17 | ./folder # and all below 18 | ]; 19 | }; 20 | } 21 | ``` 22 | 23 | # Debug Filter 24 | 25 | ```nix 26 | # flake.nix 27 | { 28 | inputs.incl.url = "github:divnix/incl"; 29 | inputs.incl.inputs.nixpkgs.follows = "nixpkgs"; 30 | 31 | outputs = { incl, self }: { 32 | filteredSource = (incl // {debug = true;}) self [ 33 | ./README.md 34 | ./folder # and all below 35 | ]; 36 | }; 37 | } 38 | ``` 39 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | 6 | # Example 7 | -------------------------------------------------------------------------------- /example/flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "incl": { 4 | "inputs": { 5 | "nixlib": "nixlib" 6 | }, 7 | "locked": { 8 | "lastModified": 0, 9 | "narHash": "sha256-yNpYwvbDB9iYj8N2XwJA+/hRdD2+hvIyrUw/iGSNaRA=", 10 | "path": "../.", 11 | "type": "path" 12 | }, 13 | "original": { 14 | "path": "../.", 15 | "type": "path" 16 | } 17 | }, 18 | "nixlib": { 19 | "locked": { 20 | "lastModified": 1667696192, 21 | "narHash": "sha256-hOdbIhnpWvtmVynKcsj10nxz9WROjZja+1wRAJ/C9+s=", 22 | "owner": "nix-community", 23 | "repo": "nixpkgs.lib", 24 | "rev": "babd9cd2ca6e413372ed59fbb1ecc3c3a5fd3e5b", 25 | "type": "github" 26 | }, 27 | "original": { 28 | "owner": "nix-community", 29 | "repo": "nixpkgs.lib", 30 | "type": "github" 31 | } 32 | }, 33 | "root": { 34 | "inputs": { 35 | "incl": "incl" 36 | } 37 | } 38 | }, 39 | "root": "root", 40 | "version": 7 41 | } 42 | -------------------------------------------------------------------------------- /example/flake.nix: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 The Standard Authors 2 | # 3 | # SPDX-License-Identifier: Unlicense 4 | { 5 | # inputs.incl.url = "github:divnix/incl"; 6 | inputs.incl.url = "path:../."; 7 | 8 | outputs = {incl, self}: { 9 | filteredSource = (incl // {debug = true;}) ./. [ 10 | ./README.md 11 | ./folder/other # and all below 12 | "foo10" # but not foo1 13 | ]; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /example/folder/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divnix/incl/526751ad3d1e23b07944b14e3f6b7a5948d3007b/example/folder/.gitkeep -------------------------------------------------------------------------------- /example/folder/not-me: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divnix/incl/526751ad3d1e23b07944b14e3f6b7a5948d3007b/example/folder/not-me -------------------------------------------------------------------------------- /example/folder/other.not-me: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divnix/incl/526751ad3d1e23b07944b14e3f6b7a5948d3007b/example/folder/other.not-me -------------------------------------------------------------------------------- /example/folder/other/file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/divnix/incl/526751ad3d1e23b07944b14e3f6b7a5948d3007b/example/folder/other/file -------------------------------------------------------------------------------- /example/foo1/dummy.nix: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /example/foo10/diskoConfigurations.nix: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixlib": { 4 | "locked": { 5 | "lastModified": 1667696192, 6 | "narHash": "sha256-hOdbIhnpWvtmVynKcsj10nxz9WROjZja+1wRAJ/C9+s=", 7 | "owner": "nix-community", 8 | "repo": "nixpkgs.lib", 9 | "rev": "babd9cd2ca6e413372ed59fbb1ecc3c3a5fd3e5b", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "nix-community", 14 | "repo": "nixpkgs.lib", 15 | "type": "github" 16 | } 17 | }, 18 | "root": { 19 | "inputs": { 20 | "nixlib": "nixlib" 21 | } 22 | } 23 | }, 24 | "root": "root", 25 | "version": 7 26 | } 27 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 The Standard Authors 2 | # SPDX-FileCopyrightText: 2022 Michael Fellinger 3 | # 4 | # SPDX-License-Identifier: MIT 5 | { 6 | inputs.nixlib.url = "github:nix-community/nixpkgs.lib"; 7 | outputs = {nixlib, ...}: let 8 | l = nixlib.lib // builtins; 9 | pretty = l.generators.toPretty {}; 10 | 11 | /* 12 | A source inclusion helper. 13 | 14 | With incl, you can specify what files should become part of the 15 | input hashing function of nix. 16 | 17 | That means, that only if that hash changes, a rebuild is triggered. 18 | 19 | By only including the sources that are an actual ingredient to your 20 | build process, you can greatly reduce the need for arbitrary builds. 21 | 22 | Slightly less effective than language native build caching. But hey, 23 | it's 100% polyglot. 24 | 25 | You can use this function independently of the rest of std. 26 | */ 27 | 28 | incl = debug: src: allowedPaths: let 29 | src' = l.unsafeDiscardStringContext (toString src); 30 | normalizedPaths = 31 | l.map ( 32 | path: let 33 | path' = l.unsafeDiscardStringContext (toString path); 34 | in 35 | if l.hasPrefix l.storeDir path' 36 | then path' 37 | else src' + "/${path'}" 38 | ) 39 | allowedPaths; 40 | patterns = 41 | l.traceIf debug "allowedPaths: ${pretty normalizedPaths}" 42 | l.traceIf 43 | debug "src: \"${src'}\"" 44 | mkInclusive 45 | normalizedPaths; 46 | filter = 47 | l.traceIf debug "patterns: ${pretty patterns}" 48 | (isIncluded debug) 49 | patterns; 50 | in 51 | l.cleanSourceWith { 52 | name = "incl"; 53 | inherit src filter; 54 | }; 55 | 56 | mkInclusive = paths: 57 | l.foldl' ( 58 | sum: path: { 59 | prefixes = sum.prefixes ++ [path]; 60 | } 61 | ) { 62 | prefixes = []; 63 | } 64 | paths; 65 | 66 | isIncluded = debug: patterns: _path: _type: let 67 | traceCandidate = l.traceIf debug "candidate ${_type}: ${_path}"; 68 | in 69 | traceCandidate ( 70 | # add file or recurse into node ? 71 | l.any ( 72 | pre: let 73 | contains = _type == "directory" && l.hasPrefix "${_path}/" pre; 74 | hit = pre == _path; 75 | prefix = l.hasPrefix "${pre}/" _path; 76 | in 77 | l.traceIf (debug && (hit || prefix || contains)) ( 78 | if contains && !(hit || prefix) 79 | then "\trecurse as container for: ${pre}" 80 | else if _type == "directory" 81 | then "\trecurse on prefix: ${pre}" 82 | else if _type == "regular" && hit 83 | then "\tinclude on hit: ${pre}" 84 | else if _type == "regular" && prefix 85 | then "\tinclude on prefix: ${pre}/" 86 | else "\tfile type '${_type}' - will fail" 87 | ) 88 | hit 89 | || prefix 90 | || contains 91 | ) 92 | patterns.prefixes 93 | ); 94 | in { 95 | debug = false; 96 | __functor = {debug, ...}: incl debug; 97 | }; 98 | } 99 | --------------------------------------------------------------------------------