forked from
lass.pds.lassul.us/superconfig
This repository has no description
5.9 kB
203 lines
1{ lib, pkgs, ... }:
2{
3 /**
4 Create a wrapped application that preserves all original outputs (man pages, completions, etc.)
5
6 # Arguments
7
8 - `package`: The package to wrap
9 - `runtimeInputs`: List of packages to add to PATH (optional)
10 - `env`: Attribute set of environment variables to export (optional)
11 - `flags`: Attribute set of command-line flags to add (optional)
12 - `flagSeparator`: Separator between flag names and values (optional, defaults to " ")
13 - `preHook`: Shell script to run before executing the command (optional)
14 - `passthru`: Attribute set to pass through to the wrapped derivation (optional)
15 - `wrapper`: Custom wrapper function (optional, defaults to exec'ing the original binary with flags)
16 - Called with { env, flags, envString, flagsString, exePath, preHook }
17
18 # Example
19
20 ```nix
21 makeWrapper pkgs.curl {
22 runtimeInputs = [ pkgs.jq ];
23 env = {
24 CURL_CA_BUNDLE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
25 };
26 flags = {
27 "--silent" = { }; # becomes --silent
28 "--connect-timeout" = "30"; # becomes --connect-timeout 30
29 };
30 preHook = ''
31 echo "Making request..." >&2
32 '';
33 }
34
35 # Or with custom wrapper:
36 makeWrapper pkgs.someProgram {
37 wrapper = { exePath, flagsString, envString, preHook, ... }: ''
38 ${envString}
39 ${preHook}
40 echo "Custom logic here"
41 exec ${exePath} ${flagsString} "$@"
42 '';
43 }
44 ```
45 */
46 makeWrapper =
47 package:
48 {
49 runtimeInputs ? [ ],
50 env ? { },
51 flags ? { },
52 flagSeparator ? " ", # " " for "--flag value" or "=" for "--flag=value"
53 preHook ? "",
54 passthru ? { },
55 wrapper ? (
56 {
57 exePath,
58 flagsString,
59 envString,
60 preHook,
61 ...
62 }:
63 ''
64 ${envString}
65 ${preHook}
66 exec ${exePath}${flagsString} "$@"
67 ''
68 ),
69 }:
70 let
71 # Extract binary name from the exe path
72 exePath = lib.getExe package;
73 binName = baseNameOf exePath;
74
75 # Generate environment variable exports
76 envString =
77 if env == { } then
78 ""
79 else
80 lib.concatStringsSep "\n" (
81 lib.mapAttrsToList (name: value: ''export ${name}="${toString value}"'') env
82 )
83 + "\n";
84
85 # Generate flag arguments with proper line breaks and indentation
86 flagsString =
87 if flags == { } then
88 ""
89 else
90 " \\\n "
91 + lib.concatStringsSep " \\\n " (
92 lib.mapAttrsToList (
93 name: value:
94 if value == { } then "${name}" else "${name}${flagSeparator}${lib.escapeShellArg (toString value)}"
95 ) flags
96 );
97
98 finalWrapper = wrapper {
99 inherit
100 env
101 flags
102 envString
103 flagsString
104 exePath
105 preHook
106 ;
107 };
108
109 # Multi-output aware symlink join function
110 multiOutputSymlinkJoin =
111 {
112 name,
113 paths,
114 outputs ? [ "out" ],
115 originalOutputs ? { },
116 passthru ? { },
117 meta ? { },
118 ...
119 }@args:
120 pkgs.stdenv.mkDerivation (
121 {
122 inherit name outputs;
123
124 buildCommand = ''
125 # Symlink all paths to the main output
126 mkdir -p $out
127 for path in ${lib.concatStringsSep " " (map toString paths)}; do
128 ${pkgs.lndir}/bin/lndir -silent "$path" $out
129 done
130
131 # Handle additional outputs by symlinking from the original package's outputs
132 ${lib.concatMapStringsSep "\n" (
133 output:
134 if output != "out" && originalOutputs ? ${output} && originalOutputs.${output} != null then
135 ''
136 if [[ -n "''${${output}:-}" ]]; then
137 mkdir -p ${"$" + output}
138 # Only symlink from the original package's corresponding output
139 ${pkgs.lndir}/bin/lndir -silent "${originalOutputs.${output}}" ${"$" + output}
140 fi
141 ''
142 else
143 ""
144 ) outputs}
145 '';
146
147 inherit passthru meta;
148 }
149 // (removeAttrs args [
150 "name"
151 "paths"
152 "outputs"
153 "originalOutputs"
154 "passthru"
155 "meta"
156 ])
157 );
158
159 # Get original package outputs for symlinking
160 originalOutputs =
161 if package ? outputs then
162 lib.listToAttrs (
163 map (output: {
164 name = output;
165 value = if package ? ${output} then package.${output} else null;
166 }) package.outputs
167 )
168 else
169 { };
170
171 # Create the wrapper derivation using our multi-output aware symlink join
172 wrappedPackage = multiOutputSymlinkJoin (
173 {
174 name = package.pname or package.name;
175 paths = [
176 (pkgs.writeShellApplication {
177 name = binName;
178 runtimeInputs = runtimeInputs;
179 text = finalWrapper;
180 })
181 package
182 ];
183 outputs = if package ? outputs then package.outputs else [ "out" ];
184 inherit originalOutputs;
185 passthru =
186 (package.passthru or { })
187 // passthru
188 // {
189 inherit env flags preHook;
190 };
191 # Pass through original attributes
192 meta = package.meta or { };
193 }
194 // lib.optionalAttrs (package ? version) {
195 inherit (package) version;
196 }
197 // lib.optionalAttrs (package ? pname) {
198 inherit (package) pname;
199 }
200 );
201 in
202 wrappedPackage;
203}