This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

superconfig / wrapperModules / flake-module.nix
12 kB 379 lines
1{ self, lib, ... }: 2{ 3 imports = [ 4 ./mpv/default.nix 5 ./notmuch/default.nix 6 ]; 7 flake.options.wrapperModules = lib.mkOption { 8 type = lib.types.attrsOf (lib.types.attrsOf lib.types.unspecified); 9 default = { }; 10 description = "Wrapper modules"; 11 }; 12 /** 13 A function to create a wrapper module. 14 returns an attribute set with options and apply function. 15 16 Example usage: 17 helloWrapper = mkWrapper (wlib: { config, ... }: { 18 options.greeting = lib.mkOption { 19 type = lib.types.str; 20 default = "hello"; 21 }; 22 config.package = config.pkgs.hello; 23 config.flags = { 24 "--greeting" = config.greeting; 25 }; 26 }; 27 28 helloWrapper.apply { 29 pkgs = pkgs; 30 greeting = "hi"; 31 }; 32 33 # This will return a derivation that wraps the hello package with the --greeting flag set to "hi". 34 */ 35 flake.config.wrapLib.mkWrapper = 36 declarationArgs: 37 let 38 declaration = declarationArgs wrapperLib; 39 40 wrapperLib = { 41 types = { 42 inherit file; 43 }; 44 }; 45 file = 46 # we need to pass pkgs here, because writeText is in pkgs 47 pkgs: 48 lib.types.submodule ( 49 { name, config, ... }: 50 { 51 options = { 52 content = lib.mkOption { 53 type = lib.types.lines; 54 description = '' 55 content of file 56 ''; 57 }; 58 path = lib.mkOption { 59 type = lib.types.path; 60 description = '' 61 the path to the file 62 ''; 63 default = pkgs.writeText name config.content; 64 defaultText = "pkgs.writeText name <content>"; 65 }; 66 }; 67 } 68 ); 69 evalArgs = cfg: { 70 modules = [ 71 declaration 72 cfg 73 { 74 options = { 75 pkgs = lib.mkOption { 76 description = '' 77 The nixpkgs pkgs instance to use. 78 We want to have this, so wrapper modules can be system agnostic. 79 ''; 80 }; 81 package = lib.mkOption { 82 type = lib.types.package; 83 description = '' 84 The base package to wrap. 85 This means we inherit all other files from this package 86 (like man page, /share, ...) 87 ''; 88 }; 89 extraPackages = lib.mkOption { 90 type = lib.types.listOf lib.types.package; 91 default = [ ]; 92 description = '' 93 Additional packages to add to the wrapper's runtime dependencies. 94 This is useful if the wrapped program needs additional libraries or tools to function correctly. 95 These packages will be added to the wrapper's runtime dependencies, ensuring they are available when the wrapped program is executed. 96 ''; 97 }; 98 flags = lib.mkOption { 99 type = lib.types.attrsOf lib.types.unspecified; # TODO add list handling 100 default = { }; 101 description = '' 102 Flags to pass to the wrapper. 103 The key is the flag name, the value is the flag value. 104 If the value is true, the flag will be passed without a value. 105 If the value is false or null, the flag will not be passed. 106 If the value is a list, the flag will be passed multiple times with each value. 107 ''; 108 }; 109 env = lib.mkOption { 110 type = lib.types.attrsOf lib.types.str; 111 default = { }; 112 description = '' 113 Environment variables to set in the wrapper. 114 ''; 115 }; 116 passthru = lib.mkOption { 117 type = lib.types.attrs; 118 default = { }; 119 description = '' 120 Additional attributes to add to the resulting derivation's passthru. 121 This can be used to add additional metadata or functionality to the wrapped package. 122 This will always contain options, config and settings, so these are reserved names and cannot be used here. 123 ''; 124 }; 125 }; 126 } 127 ]; 128 }; 129 in 130 { 131 # expose options to generate documentation of available modules 132 options = (lib.evalModules (evalArgs { })).options; 133 apply = 134 settings: 135 let 136 eval = lib.evalModules (evalArgs { 137 config = settings; 138 }); 139 options = eval.options; 140 config = eval.config; 141 in 142 self.wrapLib.makeWrapper { 143 pkgs = config.pkgs; 144 package = config.package; 145 runtimeInputs = config.extraPackages; 146 flagSeparator = "="; 147 flags = config.flags; 148 env = config.env; 149 passthru = { 150 inherit options config settings; 151 } 152 // config.passthru; 153 }; 154 }; 155 156 /** 157 Create a wrapped application that preserves all original outputs (man pages, completions, etc.) 158 159 # Arguments 160 161 - `pkgs`: The nixpkgs pkgs instance to use 162 - `package`: The package to wrap 163 - `runtimeInputs`: List of packages to add to PATH (optional) 164 - `env`: Attribute set of environment variables to export (optional) 165 - `flags`: Attribute set of command-line flags to add (optional) 166 - `flagSeparator`: Separator between flag names and values (optional, defaults to " ") 167 - `preHook`: Shell script to run before executing the command (optional) 168 - `passthru`: Attribute set to pass through to the wrapped derivation (optional) 169 - `aliases`: List of additional names to symlink to the wrapped executable (optional) 170 - `wrapper`: Custom wrapper function (optional, defaults to exec'ing the original binary with flags) 171 - Called with { env, flags, envString, flagsString, exePath, preHook } 172 173 # Example 174 175 ```nix 176 makeWrapper { 177 pkgs = pkgs; 178 package = pkgs.curl; 179 runtimeInputs = [ pkgs.jq ]; 180 env = { 181 CURL_CA_BUNDLE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; 182 }; 183 flags = { 184 "--silent" = { }; # becomes --silent 185 "--connect-timeout" = "30"; # becomes --connect-timeout 30 186 }; 187 preHook = '' 188 echo "Making request..." >&2 189 ''; 190 } 191 192 # Or with custom wrapper: 193 makeWrapper pkgs.someProgram { 194 wrapper = { exePath, flagsString, envString, preHook, ... }: '' 195 ${envString} 196 ${preHook} 197 echo "Custom logic here" 198 exec ${exePath} ${flagsString} "$@" 199 ''; 200 } 201 ``` 202 */ 203 flake.config.wrapLib.makeWrapper = 204 { 205 pkgs, 206 package, 207 runtimeInputs ? [ ], 208 env ? { }, 209 flags ? { }, 210 flagSeparator ? " ", # " " for "--flag value" or "=" for "--flag=value" 211 preHook ? "", 212 passthru ? { }, 213 aliases ? [ ], 214 wrapper ? ( 215 { 216 exePath, 217 flagsString, 218 envString, 219 preHook, 220 ... 221 }: 222 '' 223 ${envString} 224 ${preHook} 225 exec ${exePath}${flagsString} "$@" 226 '' 227 ), 228 }: 229 let 230 # Extract binary name from the exe path 231 exePath = lib.getExe package; 232 binName = baseNameOf exePath; 233 234 # Generate environment variable exports 235 envString = 236 if env == { } then 237 "" 238 else 239 lib.concatStringsSep "\n" ( 240 lib.mapAttrsToList (name: value: ''export ${name}="${toString value}"'') env 241 ) 242 + "\n"; 243 244 # Generate flag arguments with proper line breaks and indentation 245 flagsString = 246 if flags == { } then 247 "" 248 else 249 " \\\n " 250 + lib.concatStringsSep " \\\n " ( 251 lib.mapAttrsToList ( 252 name: value: 253 if value == { } then "${name}" else "${name}${flagSeparator}${lib.escapeShellArg (toString value)}" 254 ) flags 255 ); 256 257 finalWrapper = wrapper { 258 inherit 259 env 260 flags 261 envString 262 flagsString 263 exePath 264 preHook 265 ; 266 }; 267 268 # Multi-output aware symlink join function 269 multiOutputSymlinkJoin = 270 { 271 name, 272 paths, 273 outputs ? [ "out" ], 274 originalOutputs ? { }, 275 passthru ? { }, 276 meta ? { }, 277 aliases ? [ ], 278 binName ? null, 279 ... 280 }@args: 281 pkgs.stdenv.mkDerivation ( 282 { 283 inherit name outputs; 284 285 buildCommand = '' 286 # Symlink all paths to the main output 287 mkdir -p $out 288 for path in ${lib.concatStringsSep " " (map toString paths)}; do 289 ${pkgs.lndir}/bin/lndir -silent "$path" $out 290 done 291 292 # Create symlinks for aliases 293 ${lib.optionalString (aliases != [ ] && binName != null) '' 294 mkdir -p $out/bin 295 for alias in ${lib.concatStringsSep " " (map lib.escapeShellArg aliases)}; do 296 ln -sf ${lib.escapeShellArg binName} $out/bin/$alias 297 done 298 ''} 299 300 # Handle additional outputs by symlinking from the original package's outputs 301 ${lib.concatMapStringsSep "\n" ( 302 output: 303 if output != "out" && originalOutputs ? ${output} && originalOutputs.${output} != null then 304 '' 305 if [[ -n "''${${output}:-}" ]]; then 306 mkdir -p ${"$" + output} 307 # Only symlink from the original package's corresponding output 308 ${pkgs.lndir}/bin/lndir -silent "${originalOutputs.${output}}" ${"$" + output} 309 fi 310 '' 311 else 312 "" 313 ) outputs} 314 ''; 315 316 inherit passthru meta; 317 } 318 // (removeAttrs args [ 319 "name" 320 "paths" 321 "outputs" 322 "originalOutputs" 323 "passthru" 324 "meta" 325 "aliases" 326 "binName" 327 ]) 328 ); 329 330 # Get original package outputs for symlinking 331 originalOutputs = 332 if package ? outputs then 333 lib.listToAttrs ( 334 map (output: { 335 name = output; 336 value = if package ? ${output} then package.${output} else null; 337 }) package.outputs 338 ) 339 else 340 { }; 341 342 # Create the wrapper derivation using our multi-output aware symlink join 343 wrappedPackage = multiOutputSymlinkJoin ( 344 { 345 name = package.pname or package.name; 346 paths = [ 347 (pkgs.writeShellApplication { 348 name = binName; 349 runtimeInputs = runtimeInputs; 350 text = finalWrapper; 351 }) 352 package 353 ]; 354 outputs = if package ? outputs then package.outputs else [ "out" ]; 355 inherit originalOutputs aliases binName; 356 passthru = 357 (package.passthru or { }) 358 // passthru 359 // { 360 inherit 361 env 362 flags 363 preHook 364 aliases 365 ; 366 }; 367 # Pass through original attributes 368 meta = package.meta or { }; 369 } 370 // lib.optionalAttrs (package ? version) { 371 inherit (package) version; 372 } 373 // lib.optionalAttrs (package ? pname) { 374 inherit (package) pname; 375 } 376 ); 377 in 378 wrappedPackage; 379}