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.bashNonInteractive}"; }) name
334 else
335 makeScriptWriter { interpreter = "${lib.getExe pkgs.bashNonInteractive}"; } 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 (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 (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 "," 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 optionFormat = optionName: {
816 option = "--${optionName}";
817 sep = ":";
818 explicitBool = false;
819 };
820
821 nimCompileCmdArgs = lib.cli.toCommandLineShell optionFormat (
822 {
823 d = "release";
824 nimcache = ".";
825 }
826 // nimCompileOptions
827 );
828 in
829 makeBinWriter {
830 compileScript = ''
831 cp $contentPath tmp.nim
832 ${lib.getExe nim} compile ${nimCompileCmdArgs} tmp.nim
833 mv tmp $out
834 '';
835 inherit makeWrapperArgs strip;
836 } name;
837
838 /**
839 writeNimBin takes the same arguments as writeNim but outputs a directory
840 (like writeScriptBin)
841 */
842 writeNimBin = name: writeNim "/bin/${name}";
843
844 /**
845 Like writeScript but the first line is a shebang to nu
846
847 Can be called with or without extra arguments.
848
849 # Examples
850 :::{.example}
851 ## `pkgs.writers.writeNu` without arguments
852
853 ```nix
854 writeNu "example" ''
855 echo hello world
856 ''
857 ```
858 :::
859
860 :::{.example}
861 ## `pkgs.writers.writeNu` with arguments
862
863 ```nix
864 writeNu "example"
865 {
866 makeWrapperArgs = [
867 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
868 ];
869 }
870 ''
871 hello
872 ''
873 ```
874 :::
875 */
876 writeNu =
877 name: argsOrScript:
878 if lib.isAttrs argsOrScript && !lib.isDerivation argsOrScript then
879 makeScriptWriter (
880 argsOrScript // { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; }
881 ) name
882 else
883 makeScriptWriter { interpreter = "${lib.getExe pkgs.nushell} --no-config-file"; } name argsOrScript;
884
885 /**
886 Like writeScriptBin but the first line is a shebang to nu
887
888 Can be called with or without extra arguments.
889
890 # Examples
891 :::{.example}
892 ## `pkgs.writers.writeNuBin` without arguments
893
894 ```nix
895 writeNuBin "example" ''
896 echo hello world
897 ''
898 ```
899 :::
900
901 :::{.example}
902 ## `pkgs.writers.writeNuBin` with arguments
903
904 ```nix
905 writeNuBin "example"
906 {
907 makeWrapperArgs = [
908 "--prefix" "PATH" ":" "${lib.makeBinPath [ pkgs.hello ]}"
909 ];
910 }
911 ''
912 hello
913 ''
914 ```
915 :::
916 */
917 writeNuBin = name: writeNu "/bin/${name}";
918
919 /**
920 makeRubyWriter takes ruby and compatible rubyPackages and produces ruby script writer,
921 If any libraries are specified, ruby.withPackages is used as interpreter, otherwise the "bare" ruby is used.
922 */
923 makeRubyWriter =
924 ruby: rubyPackages: buildRubyPackages: name:
925 {
926 libraries ? [ ],
927 ...
928 }@args:
929 makeScriptWriter (
930 (removeAttrs args [ "libraries" ])
931 // {
932 interpreter =
933 if libraries == [ ] then "${ruby}/bin/ruby" else "${(ruby.withPackages (ps: libraries))}/bin/ruby";
934 # Rubocop doesn't seem to like running in this fashion.
935 #check = (writeDash "rubocop.sh" ''
936 # exec ${lib.getExe buildRubyPackages.rubocop} "$1"
937 #'');
938 }
939 ) name;
940
941 /**
942 Like writeScript but the first line is a shebang to ruby
943
944 # Examples
945 :::{.example}
946 ## `pkgs.writers.writeRuby` usage example
947
948 ```nix
949 writeRuby "example" { libraries = [ pkgs.rubyPackages.git ]; } ''
950 puts "hello world"
951 ''
952 ```
953
954 :::
955 */
956 writeRuby = makeRubyWriter pkgs.ruby pkgs.rubyPackages buildPackages.rubyPackages;
957
958 writeRubyBin = name: writeRuby "/bin/${name}";
959
960 /**
961 makeLuaWriter takes lua and compatible luaPackages and produces lua script writer,
962 which validates the script with luacheck at build time. If any libraries are specified,
963 lua.withPackages is used as interpreter, otherwise the "bare" lua is used.
964 */
965 makeLuaWriter =
966 lua: luaPackages: buildLuaPackages: name:
967 {
968 libraries ? [ ],
969 ...
970 }@args:
971 makeScriptWriter (
972 (removeAttrs args [ "libraries" ])
973 // {
974 interpreter = lua.interpreter;
975 # if libraries == []
976 # then lua.interpreter
977 # else (lua.withPackages (ps: libraries)).interpreter
978 # This should support packages! I just cant figure out why some dependency collision happens whenever I try to run this.
979 check = (
980 writeDash "luacheck.sh" ''
981 exec ${buildLuaPackages.luacheck}/bin/luacheck "$1"
982 ''
983 );
984 }
985 ) name;
986
987 /**
988 writeLua takes a name an attributeset with libraries and some lua source code and
989 returns an executable (should also work with luajit)
990
991 # Examples
992 :::{.example}
993 ## `pkgs.writers.writeLua` usage example
994
995 ```nix
996 writeLua "test_lua" { libraries = [ pkgs.luaPackages.say ]; } ''
997 s = require("say")
998 s:set_namespace("en")
999
1000 s:set('money', 'I have %s dollars')
1001 s:set('wow', 'So much money!')
1002
1003 print(s('money', {1000})) -- I have 1000 dollars
1004
1005 s:set_namespace("fr") -- switch to french!
1006 s:set('wow', "Tant d'argent!")
1007
1008 print(s('wow')) -- Tant d'argent!
1009 s:set_namespace("en") -- switch back to english!
1010 print(s('wow')) -- So much money!
1011 ''
1012 ```
1013
1014 :::
1015 */
1016 writeLua = makeLuaWriter pkgs.lua pkgs.luaPackages buildPackages.luaPackages;
1017
1018 writeLuaBin = name: writeLua "/bin/${name}";
1019
1020 writeRust =
1021 name:
1022 {
1023 makeWrapperArgs ? [ ],
1024 rustc ? pkgs.rustc,
1025 rustcArgs ? [ ],
1026 strip ? true,
1027 }:
1028 let
1029 darwinArgs = lib.optionals stdenv.hostPlatform.isDarwin [ "-L${lib.getLib libiconv}/lib" ];
1030 in
1031 makeBinWriter {
1032 compileScript = ''
1033 cp "$contentPath" tmp.rs
1034 PATH=${lib.makeBinPath [ pkgs.gcc ]} ${rustc}/bin/rustc ${lib.escapeShellArgs rustcArgs} ${lib.escapeShellArgs darwinArgs} -o "$out" tmp.rs
1035 '';
1036 inherit makeWrapperArgs strip;
1037 } name;
1038
1039 writeRustBin = name: writeRust "/bin/${name}";
1040
1041 /**
1042 writeJS takes a name an attributeset with libraries and some JavaScript sourcecode and
1043 returns an executable
1044
1045 # Inputs
1046
1047 `name`
1048
1049 : 1\. Function argument
1050
1051 `attrs`
1052
1053 : 2\. Function argument
1054
1055 `content`
1056
1057 : 3\. Function argument
1058
1059 # Examples
1060 :::{.example}
1061 ## `pkgs.writers.writeJS` usage example
1062
1063 ```nix
1064 writeJS "example" { libraries = [ pkgs.uglify-js ]; } ''
1065 var UglifyJS = require("uglify-js");
1066 var code = "function add(first, second) { return first + second; }";
1067 var result = UglifyJS.minify(code);
1068 console.log(result.code);
1069 ''
1070 ```
1071
1072 :::
1073 */
1074 writeJS =
1075 name:
1076 {
1077 libraries ? [ ],
1078 }:
1079 content:
1080 let
1081 node-env = pkgs.buildEnv {
1082 name = "node";
1083 paths = libraries;
1084 pathsToLink = [ "/lib/node_modules" ];
1085 };
1086 in
1087 writeDash name ''
1088 export NODE_PATH=${node-env}/lib/node_modules
1089 exec ${lib.getExe pkgs.nodejs} ${pkgs.writeText "js" content} "$@"
1090 '';
1091
1092 /**
1093 writeJSBin takes the same arguments as writeJS but outputs a directory (like writeScriptBin)
1094 */
1095 writeJSBin = name: writeJS "/bin/${name}";
1096
1097 awkFormatNginx = builtins.toFile "awkFormat-nginx.awk" ''
1098 awk -f
1099 {sub(/^[ \t]+/,"");idx=0}
1100 /\{/{ctx++;idx=1}
1101 /\}/{ctx--}
1102 {id="";for(i=idx;i<ctx;i++)id=sprintf("%s%s", id, "\t");printf "%s%s\n", id, $0}
1103 '';
1104
1105 writeNginxConfig =
1106 name: text:
1107 pkgs.runCommandLocal name
1108 {
1109 inherit text;
1110 passAsFile = [ "text" ];
1111 nativeBuildInputs = [ gixy ];
1112 } # sh
1113 ''
1114 # nginx-config-formatter has an error - https://github.com/1connect/nginx-config-formatter/issues/16
1115 awk -f ${awkFormatNginx} "$textPath" | sed '/^\s*$/d' > $out
1116 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)
1117 '';
1118
1119 /**
1120 writePerl takes a name an attributeset with libraries and some perl sourcecode and
1121 returns an executable
1122
1123 # Examples
1124 :::{.example}
1125 ## `pkgs.writers.writePerl` usage example
1126
1127 ```nix
1128 writePerl "example" { libraries = [ pkgs.perlPackages.boolean ]; } ''
1129 use boolean;
1130 print "Howdy!\n" if true;
1131 ''
1132 ```
1133
1134 :::
1135 */
1136 writePerl =
1137 name:
1138 {
1139 libraries ? [ ],
1140 ...
1141 }@args:
1142 makeScriptWriter (
1143 (removeAttrs args [ "libraries" ])
1144 // {
1145 interpreter = "${lib.getExe (pkgs.perl.withPackages (p: libraries))}";
1146 }
1147 ) name;
1148
1149 /**
1150 writePerlBin takes the same arguments as writePerl but outputs a directory (like writeScriptBin)
1151 */
1152 writePerlBin = name: writePerl "/bin/${name}";
1153
1154 /**
1155 makePythonWriter takes python and compatible pythonPackages and produces python script writer,
1156 which validates the script with flake8 at build time. If any libraries are specified,
1157 python.withPackages is used as interpreter, otherwise the "bare" python is used.
1158
1159 # Inputs
1160
1161 `python`
1162
1163 : 1\. Function argument
1164
1165 `pythonPackages`
1166
1167 : 2\. Function argument
1168
1169 `buildPythonPackages`
1170
1171 : 3\. Function argument
1172
1173 `name`
1174
1175 : 4\. Function argument
1176
1177 `attrs`
1178
1179 : 5\. Function argument
1180 */
1181 makePythonWriter =
1182 python: pythonPackages: buildPythonPackages: name:
1183 {
1184 libraries ? [ ],
1185 flakeIgnore ? [ ],
1186 doCheck ? true,
1187 ...
1188 }@args:
1189 let
1190 ignoreAttribute =
1191 optionalString (flakeIgnore != [ ])
1192 "--ignore ${concatMapStringsSep "," escapeShellArg flakeIgnore}";
1193 in
1194 makeScriptWriter (
1195 (removeAttrs args [
1196 "libraries"
1197 "flakeIgnore"
1198 "doCheck"
1199 ])
1200 // {
1201 interpreter =
1202 if pythonPackages != pkgs.pypy2Packages || pythonPackages != pkgs.pypy3Packages then
1203 if libraries == [ ] then
1204 python.interpreter
1205 else if (lib.isFunction libraries) then
1206 (python.withPackages libraries).interpreter
1207 else
1208 (python.withPackages (ps: libraries)).interpreter
1209 else
1210 python.interpreter;
1211 check = optionalString (python.isPy3k && doCheck) (
1212 writeDash "pythoncheck.sh" ''
1213 exec ${buildPythonPackages.flake8}/bin/flake8 --show-source ${ignoreAttribute} "$1"
1214 ''
1215 );
1216 }
1217 ) name;
1218
1219 /**
1220 writePyPy2 takes a name an attributeset with libraries and some pypy2 sourcecode and
1221 returns an executable
1222
1223 # Examples
1224 :::{.example}
1225 ## `pkgs.writers.writePyPy2` usage example
1226
1227 ```nix
1228 writePyPy2 "test_pypy2" { libraries = [ pkgs.pypy2Packages.enum ]; } ''
1229 from enum import Enum
1230
1231 class Test(Enum):
1232 a = "success"
1233
1234 print Test.a
1235 ''
1236 ```
1237
1238 :::
1239 */
1240 writePyPy2 = makePythonWriter pkgs.pypy2 pkgs.pypy2Packages buildPackages.pypy2Packages;
1241
1242 /**
1243 writePyPy2Bin takes the same arguments as writePyPy2 but outputs a directory (like writeScriptBin)
1244 */
1245 writePyPy2Bin = name: writePyPy2 "/bin/${name}";
1246
1247 /**
1248 writePython3 takes a name an attributeset with libraries and some python3 sourcecode and
1249 returns an executable
1250
1251 # Examples
1252 :::{.example}
1253 ## `pkgs.writers.writePython3` usage example
1254
1255 ```nix
1256 writePython3 "test_python3" { libraries = [ pkgs.python3Packages.pyyaml ]; } ''
1257 import yaml
1258
1259 y = yaml.safe_load("""
1260 - test: success
1261 """)
1262 print(y[0]['test'])
1263 ''
1264 ```
1265
1266 :::
1267 */
1268 writePython3 = makePythonWriter pkgs.python3 pkgs.python3Packages buildPackages.python3Packages;
1269
1270 # writePython3Bin takes the same arguments as writePython3 but outputs a directory (like writeScriptBin)
1271 writePython3Bin = name: writePython3 "/bin/${name}";
1272
1273 /**
1274 writePyPy3 takes a name an attributeset with libraries and some pypy3 sourcecode and
1275 returns an executable
1276
1277 # Examples
1278 :::{.example}
1279 ## `pkgs.writers.writePyPy3` usage example
1280
1281 ```nix
1282 writePyPy3 "test_pypy3" { libraries = [ pkgs.pypy3Packages.pyyaml ]; } ''
1283 import yaml
1284
1285 y = yaml.safe_load("""
1286 - test: success
1287 """)
1288 print(y[0]['test'])
1289 ''
1290 ```
1291
1292 :::
1293 */
1294 writePyPy3 = makePythonWriter pkgs.pypy3 pkgs.pypy3Packages buildPackages.pypy3Packages;
1295
1296 /**
1297 writePyPy3Bin takes the same arguments as writePyPy3 but outputs a directory (like writeScriptBin)
1298 */
1299 writePyPy3Bin = name: writePyPy3 "/bin/${name}";
1300
1301 makeFSharpWriter =
1302 {
1303 dotnet-sdk ? pkgs.dotnet-sdk,
1304 fsi-flags ? "",
1305 libraries ? _: [ ],
1306 ...
1307 }@args:
1308 nameOrPath:
1309 let
1310 fname = last (builtins.split "/" nameOrPath);
1311 path = if strings.hasSuffix ".fsx" nameOrPath then nameOrPath else "${nameOrPath}.fsx";
1312 _nugetDeps = mkNugetDeps {
1313 name = "${fname}-nuget-deps";
1314 nugetDeps = libraries;
1315 };
1316
1317 nuget-source = mkNugetSource {
1318 name = "${fname}-nuget-source";
1319 description = "Nuget source with the dependencies for ${fname}";
1320 deps = [ _nugetDeps ];
1321 };
1322
1323 fsi = writeBash "fsi" ''
1324 set -euo pipefail
1325 export HOME=$NIX_BUILD_TOP/.home
1326 export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
1327 export DOTNET_CLI_TELEMETRY_OPTOUT=1
1328 export DOTNET_NOLOGO=1
1329 export DOTNET_SKIP_WORKLOAD_INTEGRITY_CHECK=1
1330 script="$1"; shift
1331 (
1332 ${lib.getExe dotnet-sdk} new nugetconfig
1333 ${lib.getExe dotnet-sdk} nuget disable source nuget
1334 ) > /dev/null
1335 ${lib.getExe dotnet-sdk} fsi --quiet --nologo --readline- ${fsi-flags} "$@" < "$script"
1336 '';
1337
1338 in
1339 content:
1340 makeScriptWriter
1341 (
1342 (removeAttrs args [
1343 "dotnet-sdk"
1344 "fsi-flags"
1345 "libraries"
1346 ])
1347 // {
1348 interpreter = fsi;
1349 }
1350 )
1351 path
1352 ''
1353 #i "nuget: ${nuget-source}/lib"
1354 ${content}
1355 exit 0
1356 '';
1357
1358 writeFSharp = makeFSharpWriter { };
1359
1360 writeFSharpBin = name: writeFSharp "/bin/${name}";
1361}