{ 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 } 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 } 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 ; }