nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1{
2 buildPackages,
3 gixy,
4 lib,
5 libiconv,
6 makeBinaryWrapper,
7 mkNugetDeps,
8 mkNugetSource,
9 pkgs,
10 stdenv,
11}:
12let
13 inherit (lib)
14 concatMapStringsSep
15 elem
16 escapeShellArg
17 last
18 optionalString
19 strings
20 types
21 ;
22in
23rec {
24 /**
25 `makeScriptWriter` returns a derivation which creates an executable script.
26
27 # Inputs
28
29 config (AttrSet)
30 : `interpreter` (String)
31 : the [interpreter](https://en.wikipedia.org/wiki/Shebang_(Unix)) to use for the script.
32 : `check` (String)
33 : A command to check the script. For example, this could be a linting check.
34 : `makeWrapperArgs` (Optional, [ String ], Default: [])
35 : Arguments forwarded to (`makeWrapper`)[#fun-makeWrapper].
36
37 `nameOrPath` (String)
38 : The name of the script or the path to the script.
39
40 When a `string` starting with "/" is passed, the script will be created at the specified path in $out.
41 I.e. `"/bin/hello"` will create a script at `$out/bin/hello`.
42
43 Any other `string` is interpreted as a filename.
44 It must be a [POSIX filename](https://en.wikipedia.org/wiki/Filename) starting with a letter, digit, dot, or underscore.
45 Spaces or special characters are not allowed.
46
47 `content` (String)
48 : The content of the script.
49
50 :::{.note}
51 This function is used as base implementation for other high-level writer functions.
52
53 For example, `writeBash` can (roughly) be implemented as:
54
55 ```nix
56 writeBash = makeScriptWriter { interpreter = "${pkgs.bash}/bin/bash"; }
57 ```
58 :::
59
60 # Examples
61 :::{.example}
62 ## `pkgs.writers.makeScriptWriter` dash example
63
64 ```nix-repl
65 :b makeScriptWriter { interpreter = "${pkgs.dash}/bin/dash"; } "hello" "echo hello world"
66 -> /nix/store/indvlr9ckmnv4f0ynkmasv2h4fxhand0-hello
67 ```
68
69 The above example creates a script named `hello` that outputs `hello world` when executed.
70
71 ```sh
72 > /nix/store/indvlr9ckmnv4f0ynkmasv2h4fxhand0-hello
73 hello world
74 ```
75 :::
76
77 :::{.example}
78 ## `pkgs.writers.makeScriptWriter` python example
79
80 ```nix-repl
81 :b makeScriptWriter { interpreter = "${pkgs.python3}/bin/python"; } "python-hello" "print('hello world')"
82 -> /nix/store/4kvby1hqr45ffcdrvfpnpj62hanskw93-python-hello
83 ```
84
85 ```sh
86 > /nix/store/4kvby1hqr45ffcdrvfpnpj62hanskw93-python-hello
87 hello world
88 ```
89 :::
90 */
91 makeScriptWriter =
92 {
93 interpreter,
94 check ? "",
95 makeWrapperArgs ? [ ],
96 }:
97 nameOrPath: content:
98 assert
99 (types.path.check nameOrPath)
100 || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
101 assert (types.path.check content) || (types.str.check content);
102 let
103 nameIsPath = types.path.check nameOrPath;
104 name = last (builtins.split "/" nameOrPath);
105 path = if nameIsPath then nameOrPath else "/bin/${name}";
106 # The inner derivation which creates the executable under $out/bin (never at $out directly)
107 # This is required in order to support wrapping, as wrapped programs consist of
108 # at least two files: the executable and the wrapper.
109 inner =
110 pkgs.runCommandLocal name
111 (
112 {
113 inherit makeWrapperArgs;
114 nativeBuildInputs = [ makeBinaryWrapper ];
115 meta.mainProgram = name;
116 }
117 // (
118 if (types.str.check content) then
119 {
120 inherit content interpreter;
121 passAsFile = [ "content" ];
122 }
123 else
124 {
125 inherit interpreter;
126 contentPath = content;
127 }
128 )
129 )
130 ''
131 # On darwin a script cannot be used as an interpreter in a shebang but
132 # there doesn't seem to be a limit to the size of shebang and multiple
133 # arguments to the interpreter are allowed.
134 if [[ -n "${toString pkgs.stdenvNoCC.hostPlatform.isDarwin}" ]] && isScript $interpreter
135 then
136 wrapperInterpreterLine=$(head -1 "$interpreter" | tail -c+3)
137 # Get first word from the line (note: xargs echo remove leading spaces)
138 wrapperInterpreter=$(echo "$wrapperInterpreterLine" | xargs echo | cut -d " " -f1)
139
140 if isScript $wrapperInterpreter
141 then
142 echo "error: passed interpreter ($interpreter) is a script which has another script ($wrapperInterpreter) as an interpreter, which is not supported."
143 exit 1
144 fi
145
146 # This should work as long as wrapperInterpreter is a shell, which is
147 # the case for programs wrapped with makeWrapper, like
148 # python3.withPackages etc.
149 interpreterLine="$wrapperInterpreterLine $interpreter"
150 else
151 interpreterLine=$interpreter
152 fi
153
154 echo "#! $interpreterLine" > $out
155 cat "$contentPath" >> $out
156 ${optionalString (check != "") ''
157 ${check} $out
158 ''}
159 chmod +x $out
160
161 # Relocate executable
162 # Wrap it if makeWrapperArgs are specified
163 mv $out tmp
164 mkdir -p $out/$(dirname "${path}")
165 mv tmp $out/${path}
166 if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then
167 wrapProgram $out/${path} ''${makeWrapperArgs[@]}
168 fi
169 '';
170 in
171 if nameIsPath then
172 inner
173 # In case nameOrPath is a name, the user intends the executable to be located at $out.
174 # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}.
175 # This breaks the override pattern.
176 # In case this turns out to be a problem, we can still add more magic
177 else
178 pkgs.runCommandLocal name { } ''
179 ln -s ${inner}/bin/${name} $out
180 '';
181
182 /**
183 `makeBinWriter` returns a derivation which compiles the given script into an executable format.
184
185 :::{.note}
186 This function is the base implementation for other compile language `writers`, such as `writeHaskell` and `writeRust`.
187 :::
188
189 # Inputs
190
191 config (AttrSet)
192 : `compileScript` (String)
193 : The script that compiles the given content into an executable.
194
195 : `strip` (Boolean, Default: true)
196 : Whether to [strip](https://nixos.org/manual/nixpkgs/stable/#ssec-fixup-phase) the executable or not.
197
198 : `makeWrapperArgs` (Optional, [ String ], Default: [])
199 : Arguments forwarded to (`makeWrapper`)[#fun-makeWrapper]
200
201 `nameOrPath` (String)
202 : The name of the script or the path to the script.
203
204 When a `string` starting with "/" is passed, the script will be created at the specified path in $out.
205 For example, `"/bin/hello"` will create a script at `$out/bin/hello`.
206
207 Any other `string` is interpreted as a filename.
208 It must be a [POSIX filename](https://en.wikipedia.org/wiki/Filename) starting with a letter, digit, dot, or underscore.
209 Spaces or special characters are not allowed.
210
211 # Examples
212 :::{.example}
213 ## `pkgs.writers.makeBinWriter` example
214
215 ```c
216 // main.c
217 #include <stdio.h>
218
219 int main()
220 {
221 printf("Hello, World!\n");
222 return 0;
223 }
224 ```
225
226 ```nix-repl
227 :b makeBinWriter { compileScript = "${pkgs.gcc}/bin/gcc -o $out $contentPath"; } "hello" ./main.c
228 out -> /nix/store/f6crc8mwj3lvcxqclw7n09cm8nb6kxbh-hello
229 ```
230
231 The above example creates an executable named `hello` that outputs `Hello, World!` when executed.
232
233 ```sh
234 > /nix/store/f6crc8mwj3lvcxqclw7n09cm8nb6kxbh-hello
235 Hello, World!
236 ```
237 :::
238 */
239 makeBinWriter =
240 {
241 compileScript,
242 strip ? true,
243 makeWrapperArgs ? [ ],
244 }:
245 nameOrPath: content:
246 assert
247 (types.path.check nameOrPath)
248 || (builtins.match "([0-9A-Za-z._])[0-9A-Za-z._-]*" nameOrPath != null);
249 assert (types.path.check content) || (types.str.check content);
250 let
251 nameIsPath = types.path.check nameOrPath;
252 name = last (builtins.split "/" nameOrPath);
253 path = if nameIsPath then nameOrPath else "/bin/${name}";
254 # The inner derivation which creates the executable under $out/bin (never at $out directly)
255 # This is required in order to support wrapping, as wrapped programs consist of at least two files: the executable and the wrapper.
256 inner =
257 pkgs.runCommandLocal name
258 (
259 {
260 inherit makeWrapperArgs;
261 nativeBuildInputs = [ makeBinaryWrapper ];
262 meta.mainProgram = name;
263 }
264 // (
265 if (types.str.check content) then
266 {
267 inherit content;
268 passAsFile = [ "content" ];
269 }
270 else
271 { contentPath = content; }
272 )
273 )
274 ''
275 ${compileScript}
276 ${lib.optionalString strip "${lib.getBin buildPackages.bintools-unwrapped}/bin/${buildPackages.bintools-unwrapped.targetPrefix}strip -S $out"}
277 # Sometimes binaries produced for darwin (e. g. by GHC) won't be valid
278 # mach-o executables from the get-go, but need to be corrected somehow
279 # which is done by fixupPhase.
280 ${lib.optionalString pkgs.stdenvNoCC.hostPlatform.isDarwin "fixupPhase"}
281 mv $out tmp
282 mkdir -p $out/$(dirname "${path}")
283 mv tmp $out/${path}
284 if [ -n "''${makeWrapperArgs+''${makeWrapperArgs[@]}}" ]; then
285 wrapProgram $out/${path} ''${makeWrapperArgs[@]}
286 fi
287 '';
288 in
289 if nameIsPath then
290 inner
291 # In case nameOrPath is a name, the user intends the executable to be located at $out.
292 # This is achieved by creating a separate derivation containing a symlink at $out linking to ${inner}/bin/${name}.
293 # This breaks the override pattern.
294 # In case this turns out to be a problem, we can still add more magic
295 else
296 pkgs.runCommandLocal name { } ''
297 ln -s ${inner}/bin/${name} $out
298 '';
299
300 /**
301 Like writeScript but the first line is a shebang to bash
302 Can be called with or without extra arguments.
303
304 # Examples
305 :::{.example}
306
307 ## Without arguments
308
309 ```nix
310 writeBash "example" ''
311 echo hello world
312 ''
313 ```
314
315 ## With arguments
316
317 ```nix
318 writeBash "example"
319 {
320 makeWrapperArgs = [
321 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
322 ];
323 }
324 ''
325 hello
326 ''
327 ```
328 :::
329 */
330 writeBash =
331 name: argsOrScript:
332 if lib.isAttrs argsOrScript && !lib.isDerivation argsOrScript then
333 makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.bash}"; }) name
334 else
335 makeScriptWriter { interpreter = "${lib.getExe pkgs.bash}"; } name argsOrScript;
336
337 /**
338 Like writeScriptBin but the first line is a shebang to bash
339
340 Can be called with or without extra arguments.
341
342 ## Examples
343 :::{.example}
344 ## `pkgs.writers.writeBashBin` example without arguments
345
346 ```nix
347 writeBashBin "example" ''
348 echo hello world
349 ''
350 ```
351 :::
352
353 :::{.example}
354 ## `pkgs.writers.writeBashBin` example with arguments
355
356 ```nix
357 writeBashBin "example"
358 {
359 makeWrapperArgs = [
360 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
361 ];
362 }
363 ''
364 hello
365 ''
366 ```
367 :::
368 */
369 writeBashBin = name: writeBash "/bin/${name}";
370
371 /**
372 Like writeScript but the first line is a shebang to dash
373
374 Can be called with or without extra arguments.
375
376 # Example
377 :::{.example}
378 ## `pkgs.writers.writeDash` example without arguments
379
380 ```nix
381 writeDash "example" ''
382 echo hello world
383 ''
384 ```
385 :::
386
387 :::{.example}
388 ## `pkgs.writers.writeDash` example with arguments
389
390 ```nix
391 writeDash "example"
392 {
393 makeWrapperArgs = [
394 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
395 ];
396 }
397 ''
398 hello
399 ''
400 ```
401 :::
402 */
403 writeDash =
404 name: argsOrScript:
405 if lib.isAttrs argsOrScript && !lib.isDerivation argsOrScript then
406 makeScriptWriter (argsOrScript // { interpreter = "${lib.getExe pkgs.dash}"; }) name
407 else
408 makeScriptWriter { interpreter = "${lib.getExe pkgs.dash}"; } name argsOrScript;
409
410 /**
411 Like writeScriptBin but the first line is a shebang to dash
412
413 Can be called with or without extra arguments.
414
415 # Examples
416 :::{.example}
417 ## `pkgs.writers.writeDashBin` without arguments
418
419 ```nix
420 writeDashBin "example" ''
421 echo hello world
422 ''
423 ```
424 :::
425
426 :::{.example}
427 ## `pkgs.writers.writeDashBin` with arguments
428
429 ```nix
430 writeDashBin "example"
431 {
432 makeWrapperArgs = [
433 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
434 ];
435 }
436 ''
437 hello
438 ''
439 ```
440 :::
441 */
442 writeDashBin = name: writeDash "/bin/${name}";
443
444 /**
445 Like writeScript but the first line is a shebang to fish
446
447 Can be called with or without extra arguments.
448
449 :::{.example}
450 ## `pkgs.writers.writeFish` without arguments
451
452 ```nix
453 writeFish "example" ''
454 echo hello world
455 ''
456 ```
457 :::
458
459 :::{.example}
460 ## `pkgs.writers.writeFish` with arguments
461
462 ```nix
463 writeFish "example"
464 {
465 makeWrapperArgs = [
466 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
467 ];
468 }
469 ''
470 hello
471 ''
472 ```
473 :::
474 */
475 writeFish =
476 name: argsOrScript:
477 if lib.isAttrs argsOrScript && !lib.isDerivation argsOrScript then
478 makeScriptWriter (
479 argsOrScript
480 // {
481 interpreter = "${lib.getExe pkgs.fish} --no-config";
482 check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only
483 }
484 ) name
485 else
486 makeScriptWriter {
487 interpreter = "${lib.getExe pkgs.fish} --no-config";
488 check = "${lib.getExe pkgs.fish} --no-config --no-execute"; # syntax check only
489 } name argsOrScript;
490
491 /**
492 Like writeScriptBin but the first line is a shebang to fish
493
494 Can be called with or without extra arguments.
495
496 # Examples
497 :::{.example}
498 ## `pkgs.writers.writeFishBin` without arguments
499
500 ```nix
501 writeFishBin "example" ''
502 echo hello world
503 ''
504 ```
505 :::
506
507 :::{.example}
508 ## `pkgs.writers.writeFishBin` with arguments
509
510 ```nix
511 writeFishBin "example"
512 {
513 makeWrapperArgs = [
514 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
515 ];
516 }
517 ''
518 hello
519 ''
520 ```
521 :::
522 */
523 writeFishBin = name: writeFish "/bin/${name}";
524
525 /**
526 writeBabashka takes a name, an attrset with babashka interpreter and linting check (both optional)
527 and some babashka source code and returns an executable.
528
529 `pkgs.babashka-unwrapped` is used as default interpreter for small closure size. If dependencies needed, use `pkgs.babashka` instead. Pass empty string to check to disable the default clj-kondo linting.
530
531 # Examples
532 :::{.example}
533 ## `pkgs.writers.writeBabashka` with empty arguments
534
535 ```nix
536 writeBabashka "example" { } ''
537 (println "hello world")
538 ''
539 ```
540 :::
541
542 :::{.example}
543 ## `pkgs.writers.writeBabashka` with arguments
544
545 ```nix
546 writeBabashka "example"
547 {
548 makeWrapperArgs = [
549 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
550 ];
551 }
552 ''
553 (require '[babashka.tasks :as tasks])
554 (tasks/shell "hello" "-g" "Hello babashka!")
555 ''
556 ```
557 :::
558
559 :::{.note}
560 Babashka needs Java for fetching dependencies. Wrapped babashka contains jdk,
561 pass wrapped version `pkgs.babashka` to babashka if dependencies are required.
562
563 For example:
564
565 ```nix
566 writeBabashka "example"
567 {
568 babashka = pkgs.babashka;
569 }
570 ''
571 (require '[babashka.deps :as deps])
572 (deps/add-deps '{:deps {medley/medley {:mvn/version "1.3.0"}}})
573 (require '[medley.core :as m])
574 (prn (m/index-by :id [{:id 1} {:id 2}]))
575 ''
576 ```
577 :::
578
579 :::{.note}
580 Disable clj-kondo linting:
581
582 ```nix
583 writeBabashka "example"
584 {
585 check = "";
586 }
587 ''
588 (println "hello world")
589 ''
590 ```
591 :::
592 */
593 writeBabashka =
594 name:
595 {
596 makeWrapperArgs ? [ ],
597 babashka ? pkgs.babashka-unwrapped,
598 check ? "${lib.getExe pkgs.clj-kondo} --lint",
599 ...
600 }@args:
601 makeScriptWriter (
602 (builtins.removeAttrs args [
603 "babashka"
604 ])
605 // {
606 interpreter = "${lib.getExe babashka}";
607 }
608 ) name;
609
610 /**
611 writeBabashkaBin takes the same arguments as writeBabashka but outputs a directory
612 (like writeScriptBin)
613 */
614 writeBabashkaBin = name: writeBabashka "/bin/${name}";
615
616 /**
617 `writeGuile` returns a derivation that creates an executable Guile script.
618
619 # Inputs
620
621 `nameOrPath` (String)
622 : Name of or path to the script. The semantics is the same as that of
623 `makeScriptWriter`.
624
625 `config` (AttrSet)
626 : `guile` (Optional, Derivation, Default: `pkgs.guile`)
627 : Guile package used for the script.
628 : `libraries` (Optional, [ Derivation ], Default: [])
629 : Extra Guile libraries exposed to the script.
630 : `r6rs` and `r7rs` (Optional, Boolean, Default: false)
631 : Whether to adapt Guile’s initial environment to better support R6RS/
632 R7RS. See the [Guile Reference Manual](https://www.gnu.org/software/guile/manual/html_node/index.html)
633 for details.
634 : `srfi` (Optional, [ Int ], Default: [])
635 : SRFI module to be loaded into the interpreter before evaluating a
636 script file or starting the REPL. See the Guile Reference Manual to
637 know which SRFI are supported.
638 : Other attributes are directly passed to `makeScriptWriter`.
639
640 `content` (String)
641 : Content of the script.
642
643 # Examples
644
645 :::{.example}
646 ## `pkgs.writers.writeGuile` with default config
647
648 ```nix
649 writeGuile "guile-script" { }
650 ''
651 (display "Hello, world!")
652 ''
653 ```
654 :::
655
656 :::{.example}
657 ## `pkgs.writers.writeGuile` with SRFI-1 enabled and extra libraries
658
659 ```nix
660 writeGuile "guile-script" {
661 libraries = [ pkgs.guile-semver ];
662 srfi = [ 1 ];
663 }
664 ''
665 (use-modules (semver))
666 (make-semver 1 (third '(2 3 4)) 5) ; => #<semver 1.4.5>
667 ''
668 ```
669 :::
670 */
671 writeGuile =
672 nameOrPath:
673 {
674 guile ? pkgs.guile,
675 libraries ? [ ],
676 r6rs ? false,
677 r7rs ? false,
678 srfi ? [ ],
679 ...
680 }@config:
681 content:
682 assert builtins.all builtins.isInt srfi;
683 let
684 finalGuile = pkgs.buildEnv {
685 name = "guile-env";
686 paths = [ guile ] ++ libraries;
687 passthru = {
688 inherit (guile) siteDir siteCcacheDir;
689 };
690 meta.mainProgram = guile.meta.mainProgram or "guile";
691 };
692 in
693 makeScriptWriter
694 (
695 (builtins.removeAttrs config [
696 "guile"
697 "libraries"
698 "r6rs"
699 "r7rs"
700 "srfi"
701 ])
702 // {
703 interpreter = "${lib.getExe finalGuile} \\";
704 makeWrapperArgs = [
705 "--set"
706 "GUILE_LOAD_PATH"
707 "${finalGuile}/${finalGuile.siteDir}:${finalGuile}/lib/scheme-libs"
708 "--set"
709 "GUILE_LOAD_COMPILED_PATH"
710 "${finalGuile}/${finalGuile.siteCcacheDir}:${finalGuile}/lib/libobj"
711 "--set"
712 "LD_LIBRARY_PATH"
713 "${finalGuile}/lib/ffi"
714 "--set"
715 "DYLD_LIBRARY_PATH"
716 "${finalGuile}/lib/ffi"
717 ];
718 }
719 )
720 nameOrPath
721 /*
722 Spaces, newlines and tabs are significant for the "meta switch" of Guile, so
723 certain complication must be made to ensure correctness.
724 */
725 (
726 lib.concatStringsSep "\n" [
727 (lib.concatStringsSep " " (
728 [ "--no-auto-compile" ]
729 ++ lib.optional r6rs "--r6rs"
730 ++ lib.optional r7rs "--r7rs"
731 ++ lib.optional (srfi != [ ]) ("--use-srfi=" + concatMapStringsSep "," builtins.toString srfi)
732 ++ [ "-s" ]
733 ))
734 "!#"
735 content
736 ]
737 );
738
739 /**
740 writeGuileBin takes the same arguments as writeGuile but outputs a directory
741 (like writeScriptBin)
742 */
743 writeGuileBin = name: writeGuile "/bin/${name}";
744
745 /**
746 writeHaskell takes a name, an attrset with libraries and haskell version (both optional)
747 and some haskell source code and returns an executable.
748
749 # Examples
750 :::{.example}
751 ## `pkgs.writers.writeHaskell` usage example
752
753 ```nix
754 writeHaskell "missiles" { libraries = [ pkgs.haskellPackages.acme-missiles ]; } ''
755 import Acme.Missiles
756
757 main = launchMissiles
758 '';
759 ```
760 :::
761 */
762 writeHaskell =
763 name:
764 {
765 ghc ? pkgs.ghc,
766 ghcArgs ? [ ],
767 libraries ? [ ],
768 makeWrapperArgs ? [ ],
769 strip ? true,
770 threadedRuntime ? true,
771 }:
772 let
773 appendIfNotSet = el: list: if elem el list then list else list ++ [ el ];
774 ghcArgs' = if threadedRuntime then appendIfNotSet "-threaded" ghcArgs else ghcArgs;
775
776 in
777 makeBinWriter {
778 compileScript = ''
779 cp $contentPath tmp.hs
780 ${(ghc.withPackages (_: libraries))}/bin/ghc ${lib.escapeShellArgs ghcArgs'} tmp.hs
781 mv tmp $out
782 '';
783 inherit makeWrapperArgs strip;
784 } name;
785
786 /**
787 writeHaskellBin takes the same arguments as writeHaskell but outputs a directory (like writeScriptBin)
788 */
789 writeHaskellBin = name: writeHaskell "/bin/${name}";
790
791 /**
792 writeNim takes a name, an attrset with an optional Nim compiler, and some
793 Nim source code, returning an executable.
794
795 # Examples
796 :::{.example}
797 ## `pkgs.writers.writeNim` usage example
798
799 ```nix
800 writeNim "hello-nim" { nim = pkgs.nim2; } ''
801 echo "hello nim"
802 '';
803 ```
804 :::
805 */
806 writeNim =
807 name:
808 {
809 makeWrapperArgs ? [ ],
810 nim ? pkgs.nim2,
811 nimCompileOptions ? { },
812 strip ? true,
813 }:
814 let
815 nimCompileCmdArgs = lib.cli.toGNUCommandLineShell { optionValueSeparator = ":"; } (
816 {
817 d = "release";
818 nimcache = ".";
819 }
820 // nimCompileOptions
821 );
822 in
823 makeBinWriter {
824 compileScript = ''
825 cp $contentPath tmp.nim
826 ${lib.getExe nim} compile ${nimCompileCmdArgs} tmp.nim
827 mv tmp $out
828 '';
829 inherit makeWrapperArgs strip;
830 } name;
831
832 /**
833 writeNimBin takes the same arguments as writeNim but outputs a directory
834 (like writeScriptBin)
835 */
836 writeNimBin = name: writeNim "/bin/${name}";
837
838 /**
839 Like writeScript but the first line is a shebang to nu
840
841 Can be called with or without extra arguments.
842
843 # Examples
844 :::{.example}
845 ## `pkgs.writers.writeNu` without arguments
846
847 ```nix
848 writeNu "example" ''
849 echo hello world
850 ''
851 ```
852 :::
853
854 :::{.example}
855 ## `pkgs.writers.writeNu` with arguments
856
857 ```nix
858 writeNu "example"
859 {
860 makeWrapperArgs = [
861 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
862 ];
863 }
864 ''
865 hello
866 ''
867 ```
868 :::
869 */
870 writeNu =
871 name: argsOrScript:
872 if lib.isAttrs argsOrScript && !lib.isDerivation argsOrScript then
873 makeScriptWriter (
874 argsOrScript // { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; }
875 ) name
876 else
877 makeScriptWriter { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; } name argsOrScript;
878
879 /**
880 Like writeScriptBin but the first line is a shebang to nu
881
882 Can be called with or without extra arguments.
883
884 # Examples
885 :::{.example}
886 ## `pkgs.writers.writeNuBin` without arguments
887
888 ```nix
889 writeNuBin "example" ''
890 echo hello world
891 ''
892 ```
893 :::
894
895 :::{.example}
896 ## `pkgs.writers.writeNuBin` with arguments
897
898 ```nix
899 writeNuBin "example"
900 {
901 makeWrapperArgs = [
902 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
903 ];
904 }
905 ''
906 hello
907 ''
908 ```
909 :::
910 */
911 writeNuBin = name: writeNu "/bin/${name}";
912
913 /**
914 makeRubyWriter takes ruby and compatible rubyPackages and produces ruby script writer,
915 If any libraries are specified, ruby.withPackages is used as interpreter, otherwise the "bare" ruby is used.
916 */
917 makeRubyWriter =
918 ruby: rubyPackages: buildRubyPackages: name:
919 {
920 libraries ? [ ],
921 ...
922 }@args:
923 makeScriptWriter (
924 (builtins.removeAttrs args [ "libraries" ])
925 // {
926 interpreter =
927 if libraries == [ ] then "${ruby}/bin/ruby" else "${(ruby.withPackages (ps: libraries))}/bin/ruby";
928 # Rubocop doesn't seem to like running in this fashion.
929 #check = (writeDash "rubocop.sh" ''
930 # exec ${lib.getExe buildRubyPackages.rubocop} "$1"
931 #'');
932 }
933 ) name;
934
935 /**
936 Like writeScript but the first line is a shebang to ruby
937
938 # Examples
939 :::{.example}
940 ## `pkgs.writers.writeRuby` usage example
941
942 ```nix
943 writeRuby "example" { libraries = [ pkgs.rubyPackages.git ]; } ''
944 puts "hello world"
945 ''
946 ```
947
948 :::
949 */
950 writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages;
951
952 writeRubyBin = name: writeRuby "/bin/${name}";
953
954 /**
955 makeLuaWriter takes lua and compatible luaPackages and produces lua script writer,
956 which validates the script with luacheck at build time. If any libraries are specified,
957 lua.withPackages is used as interpreter, otherwise the "bare" lua is used.
958 */
959 makeLuaWriter =
960 lua: luaPackages: buildLuaPackages: name:
961 {
962 libraries ? [ ],
963 ...
964 }@args:
965 makeScriptWriter (
966 (builtins.removeAttrs args [ "libraries" ])
967 // {
968 interpreter = lua.interpreter;
969 # if libraries == []
970 # then lua.interpreter
971 # else (lua.withPackages (ps: libraries)).interpreter
972 # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this.
973 check = (
974 writeDash "luacheck.sh" ''
975 exec ${buildLuaPackages.luacheck}/bin/luacheck "$1"
976 ''
977 );
978 }
979 ) name;
980
981 /**
982 writeLua takes a name an attributeset with libraries and some lua source code and
983 returns an executable (should also work with luajit)
984
985 # Examples
986 :::{.example}
987 ## `pkgs.writers.writeLua` usage example
988
989 ```nix
990 writeLua "test_lua" { libraries = [ pkgs.luaPackages.say ]; } ''
991 s = require("say")
992 s:set_namespace("en")
993
994 s:set('money', 'I have %s dollars')
995 s:set('wow', 'So much money!')
996
997 print(s('money', {1000})) -- I have 1000 dollars
998
999 s:set_namespace("fr") -- switch to french!
1000 s:set('wow', "Tant d'argent!")
1001
1002 print(s('wow')) -- Tant d'argent!
1003 s:set_namespace("en") -- switch back to english!
1004 print(s('wow')) -- So much money!
1005 ''
1006 ```
1007
1008 :::
1009 */
1010 writeLua = makeLuaWriter pkgs.lua pkgs.luaPackages buildPackages.luaPackages;
1011
1012 writeLuaBin = name: writeLua "/bin/${name}";
1013
1014 writeRust =
1015 name:
1016 {
1017 makeWrapperArgs ? [ ],
1018 rustc ? pkgs.rustc,
1019 rustcArgs ? [ ],
1020 strip ? true,
1021 }:
1022 let
1023 darwinArgs = lib.optionals stdenv.hostPlatform.isDarwin [ "-L${lib.getLib libiconv}/lib" ];
1024 in
1025 makeBinWriter {
1026 compileScript = ''
1027 cp "$contentPath" tmp.rs
1028 PATH=${lib.makeBinPath [ pkgs.gcc ]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs
1029 '';
1030 inherit makeWrapperArgs strip;
1031 } name;
1032
1033 writeRustBin = name: writeRust "/bin/${name}";
1034
1035 /**
1036 writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and
1037 returns an executable
1038
1039 # Inputs
1040
1041 `name`
1042
1043 : 1\. Function argument
1044
1045 `attrs`
1046
1047 : 2\. Function argument
1048
1049 `content`
1050
1051 : 3\. Function argument
1052
1053 # Examples
1054 :::{.example}
1055 ## `pkgs.writers.writeJS` usage example
1056
1057 ```nix
1058 writeJS "example" { libraries = [ pkgs.uglify-js ]; } ''
1059 var UglifyJS = require("uglify-js");
1060 var code = "function add(first, second) { return first + second; }";
1061 var result = UglifyJS.minify(code);
1062 console.log(result.code);
1063 ''
1064 ```
1065
1066 :::
1067 */
1068 writeJS =
1069 name:
1070 {
1071 libraries ? [ ],
1072 }:
1073 content:
1074 let
1075 node-env = pkgs.buildEnv {
1076 name = "node";
1077 paths = libraries;
1078 pathsToLink = [ "/lib/node_modules" ];
1079 };
1080 in
1081 writeDash name ''
1082 export NODE_PATH=${node-env}/lib/node_modules
1083 exec ${lib.getExe pkgs.nodejs} ${pkgs.writeText "js" content} "$@"
1084 '';
1085
1086 /**
1087 writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin)
1088 */
1089 writeJSBin = name: writeJS "/bin/${name}";
1090
1091 awkFormatNginx = builtins.toFile "awkFormat-nginx.awk" ''
1092 awk -f
1093 {sub(/^[ \t]+/,"");idx=0}
1094 /\{/{ctx++;idx=1}
1095 /\}/{ctx--}
1096 {id="";for(i=idx;i<ctx;i++)id=sprintf("%s%s", id, "\t");printf "%s%s\n", id, $0}
1097 '';
1098
1099 writeNginxConfig =
1100 name: text:
1101 pkgs.runCommandLocal name
1102 {
1103 inherit text;
1104 passAsFile = [ "text" ];
1105 nativeBuildInputs = [ gixy ];
1106 } # sh
1107 ''
1108 # nginx-config-formatter has an error - https://github.com/1connect/nginx-config-formatter/issues/16
1109 awk -f ${awkFormatNginx} "$textPath" | sed '/^\s*$/d' > $out
1110 gixy $out || (echo "\n\nThis can be caused by combining multiple incompatible services on the same hostname.\n\nFull merged config:\n\n"; cat $out; exit 1)
1111 '';
1112
1113 /**
1114 writePerl takes a name an attributeset with libraries and some perl sourcecode and
1115 returns an executable
1116
1117 # Examples
1118 :::{.example}
1119 ## `pkgs.writers.writePerl` usage example
1120
1121 ```nix
1122 writePerl "example" { libraries = [ pkgs.perlPackages.boolean ]; } ''
1123 use boolean;
1124 print "Howdy!\n" if true;
1125 ''
1126 ```
1127
1128 :::
1129 */
1130 writePerl =
1131 name:
1132 {
1133 libraries ? [ ],
1134 ...
1135 }@args:
1136 makeScriptWriter (
1137 (builtins.removeAttrs args [ "libraries" ])
1138 // {
1139 interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}";
1140 }
1141 ) name;
1142
1143 /**
1144 writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin)
1145 */
1146 writePerlBin = name: writePerl "/bin/${name}";
1147
1148 /**
1149 makePythonWriter takes python and compatible pythonPackages and produces python script writer,
1150 which validates the script with flake8 at build time. If any libraries are specified,
1151 python.withPackages is used as interpreter, otherwise the "bare" python is used.
1152
1153 # Inputs
1154
1155 `python`
1156
1157 : 1\. Function argument
1158
1159 `pythonPackages`
1160
1161 : 2\. Function argument
1162
1163 `buildPythonPackages`
1164
1165 : 3\. Function argument
1166
1167 `name`
1168
1169 : 4\. Function argument
1170
1171 `attrs`
1172
1173 : 5\. Function argument
1174 */
1175 makePythonWriter =
1176 python: pythonPackages: buildPythonPackages: name:
1177 {
1178 libraries ? [ ],
1179 flakeIgnore ? [ ],
1180 doCheck ? true,
1181 ...
1182 }@args:
1183 let
1184 ignoreAttribute =
1185 optionalString (flakeIgnore != [ ])
1186 "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}";
1187 in
1188 makeScriptWriter (
1189 (builtins.removeAttrs args [
1190 "libraries"
1191 "flakeIgnore"
1192 "doCheck"
1193 ])
1194 // {
1195 interpreter =
1196 if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then
1197 if libraries == [ ] then
1198 python.interpreter
1199 else if (lib.isFunction libraries) then
1200 (python.withPackages libraries).interpreter
1201 else
1202 (python.withPackages (ps: libraries)).interpreter
1203 else
1204 python.interpreter;
1205 check = optionalString (python.isPy3k && doCheck) (
1206 writeDash "pythoncheck.sh" ''
1207 exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1"
1208 ''
1209 );
1210 }
1211 ) name;
1212
1213 /**
1214 writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and
1215 returns an executable
1216
1217 # Examples
1218 :::{.example}
1219 ## `pkgs.writers.writePyPy2` usage example
1220
1221 ```nix
1222 writePyPy2 "test_pypy2" { libraries = [ pkgs.pypy2Packages.enum ]; } ''
1223 from enum import Enum
1224
1225 class Test(Enum):
1226 a = "success"
1227
1228 print Test.a
1229 ''
1230 ```
1231
1232 :::
1233 */
1234 writePyPy2 = makePythonWriter pkgs.pypy2 pkgs.pypy2Packages buildPackages.pypy2Packages;
1235
1236 /**
1237 writePyPy2Bin takes the same arguments as writePyPy2 but outputs a directory (like writeScriptBin)
1238 */
1239 writePyPy2Bin = name: writePyPy2 "/bin/${name}";
1240
1241 /**
1242 writePython3 takes a name an attributeset with libraries and some python3 sourcecode and
1243 returns an executable
1244
1245 # Examples
1246 :::{.example}
1247 ## `pkgs.writers.writePython3` usage example
1248
1249 ```nix
1250 writePython3 "test_python3" { libraries = [ pkgs.python3Packages.pyyaml ]; } ''
1251 import yaml
1252
1253 y = yaml.safe_load("""
1254 - test: success
1255 """)
1256 print(y[0]['test'])
1257 ''
1258 ```
1259
1260 :::
1261 */
1262 writePython3 = makePythonWriter pkgs.python3 pkgs.python3Packages buildPackages.python3Packages;
1263
1264 # writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin)
1265 writePython3Bin = name: writePython3 "/bin/${name}";
1266
1267 /**
1268 writePyPy3 takes a name an attributeset with libraries and some pypy3 sourcecode and
1269 returns an executable
1270
1271 # Examples
1272 :::{.example}
1273 ## `pkgs.writers.writePyPy3` usage example
1274
1275 ```nix
1276 writePyPy3 "test_pypy3" { libraries = [ pkgs.pypy3Packages.pyyaml ]; } ''
1277 import yaml
1278
1279 y = yaml.safe_load("""
1280 - test: success
1281 """)
1282 print(y[0]['test'])
1283 ''
1284 ```
1285
1286 :::
1287 */
1288 writePyPy3 = makePythonWriter pkgs.pypy3 pkgs.pypy3Packages buildPackages.pypy3Packages;
1289
1290 /**
1291 writePyPy3Bin takes the same arguments as writePyPy3 but outputs a directory (like writeScriptBin)
1292 */
1293 writePyPy3Bin = name: writePyPy3 "/bin/${name}";
1294
1295 makeFSharpWriter =
1296 {
1297 dotnet-sdk ? pkgs.dotnet-sdk,
1298 fsi-flags ? "",
1299 libraries ? _: [ ],
1300 ...
1301 }@args:
1302 nameOrPath:
1303 let
1304 fname = last (builtins.split "/" nameOrPath);
1305 path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx";
1306 _nugetDeps = mkNugetDeps {
1307 name = "${fname}-nuget-deps";
1308 nugetDeps = libraries;
1309 };
1310
1311 nuget-source = mkNugetSource {
1312 name = "${fname}-nuget-source";
1313 description = "Nuget source with the dependencies for ${fname}";
1314 deps = [ _nugetDeps ];
1315 };
1316
1317 fsi = writeBash "fsi" ''
1318 set -euo pipefail
1319 export HOME=$NIX_BUILD_TOP/.home
1320 export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
1321 export DOTNET_CLI_TELEMETRY_OPTOUT=1
1322 export DOTNET_NOLOGO=1
1323 export DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK=1
1324 script="$1"; shift
1325 (
1326 ${lib.getExe dotnet-sdk} new nugetconfig
1327 ${lib.getExe dotnet-sdk} nuget disable source nuget
1328 ) > /dev/null
1329 ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script"
1330 '';
1331
1332 in
1333 content:
1334 makeScriptWriter
1335 (
1336 (builtins.removeAttrs args [
1337 "dotnet-sdk"
1338 "fsi-flags"
1339 "libraries"
1340 ])
1341 // {
1342 interpreter = fsi;
1343 }
1344 )
1345 path
1346 ''
1347 #i "nuget: ${nuget-source}/lib"
1348 ${content}
1349 exit 0
1350 '';
1351
1352 writeFSharp = makeFSharpWriter { };
1353
1354 writeFSharpBin = name: writeFSharp "/bin/${name}";
1355}