about summary refs log tree commit diff
path: root/users/Profpatsch/nix-tools.nix
blob: 4f29274573cfc1b40961484cfc098ec16ac185ee (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
{ depot, pkgs, ... }:

let
  bins = depot.nix.getBins pkgs.nix [ "nix-build" "nix-instantiate" ];

  # TODO: both of these don’t prevent `result` from being created. good? bad?

  # Usage (execline syntax):
  #    nix-run { -A foo <more_nix_options> } args...
  #
  # Takes an execline block of `nix-build` arguments, which should produce an executable store path.
  # Then runs the store path with `prog...`.
  nix-run = depot.nix.writeExecline "nix-run" { argMode = "env"; } [
    "backtick"
    "-iE"
    "storepath"
    [
      runblock
      "1"
      bins.nix-build
    ]
    runblock
    "-r"
    "2"
    "$storepath"
  ];

  # Usage (execline syntax):
  #    nix-run-bin { -A foo <more_nix_options> } <foo_bin_name> args...
  #
  # Takes an execline block of `nix-build` arguments, which should produce a store path with a bin/ directory in it.
  # Then runs the given command line with the given arguments. All executables in the built storepath’s bin directory are prepended to `PATH`.
  nix-run-bin = depot.nix.writeExecline "nix-run-bin" { argMode = "env"; } [
    "backtick"
    "-iE"
    "storepath"
    [
      runblock
      "1"
      bins.nix-build
    ]
    "importas"
    "-ui"
    "PATH"
    "PATH"
    "export"
    "PATH"
    "\${storepath}/bin:\${PATH}"
    runblock
    "-r"
    "2"
  ];

  nix-eval = depot.nix.writeExecline "nix-eval" { } [
    bins.nix-instantiate
    "--read-write-mode"
    "--eval"
    "--strict"
    "$@"
  ];

  # This is a rewrite of execline’s runblock.
  # It adds the feature that instead of just
  # executing the block it reads, it can also
  # pass it as argv to given commands.
  #
  # This is going to be added to a future version
  # of execline by skarnet, but for now it’s easier
  # to just dirtily reimplement it in Python.
  #
  # TODO: this was added to recent execline versions,
  # but it doesn’t seem to be a drop-in replacement,
  # if I use execline’s runblock in nix-run-bin above,
  # I get errors like
  # > export: fatal: unable to exec runblock: Success
  runblock = pkgs.writers.writePython3 "runblock"
    {
      flakeIgnore = [ "E501" "E226" ];
    } ''
    import sys
    import os
    from pathlib import Path

    skip = False
    one = sys.argv[1]
    if one == "-r":
        skip = True
        block_number = int(sys.argv[2])
        block_start = 3
    elif one.startswith("-"):
        print("runblock-python: only -r supported", file=sys.stderr)
        sys.exit(100)
    else:
        block_number = int(one)
        block_start = 2

    execline_argv_no = int(os.getenvb(b"#"))
    runblock_argv = [os.getenv(str(no)) for no in range(1, execline_argv_no + 1)]


    def parse_block(args):
        new_args = []
        if args == []:
            print(
                "runblock-python: empty block",
                file=sys.stderr
            )
            sys.exit(100)
        for arg in args:
            if arg == "":
                break
            elif arg.startswith(" "):
                new_args.append(arg[1:])
            else:
                print(
                    "runblock-python: unterminated block: {}".format(args),
                    file=sys.stderr
                )
                sys.exit(100)
        args_rest = args[len(new_args)+1:]
        return (new_args, args_rest)


    if skip:
        rest = runblock_argv
        for _ in range(0, block_number-1):
            (_, rest) = parse_block(rest)
        new_argv = rest
    else:
        new_argv = []
        rest = runblock_argv
        for _ in range(0, block_number):
            (new_argv, rest) = parse_block(rest)

    given_argv = sys.argv[block_start:]
    run = given_argv + new_argv
    if os.path.isabs(run[0]):
        # TODO: ideally I’d check if it’s an executable here, but it was too hard to figure out and I couldn’t be bothered tbh
        if not Path(run[0]).is_file():
            print(
                "runblock-python: Executable {} does not exist or is not a file.".format(run[0]),
                file=sys.stderr
            )
            sys.exit(100)
    os.execvp(
        file=run[0],
        args=run
    )
  '';


in
{
  inherit
    nix-run
    nix-run-bin
    nix-eval
    ;
}