chore(nint): move from //users/sterni to //nix

Since //web/bubblegum depends on nint, we need to move it to a non user
directory to conform with the policy established via cl/3434.
Note that this likely doesn't mean greater stability (which isn't
really implied in depot anyways), since I still would like to use a more
elaborate calling convention to allow for additional useful features.

Change-Id: I616f905d8df13e3363674aab69a797b0d39fdd79
Reviewed-on: https://cl.tvl.fyi/c/depot/+/3506
Tested-by: BuildkiteCI
Reviewed-by: tazjin <mail@tazj.in>
This commit is contained in:
sterni 2021-09-09 18:26:03 +02:00
parent 5d5fb4e6a1
commit 318d10e608
8 changed files with 7 additions and 8 deletions

View file

@ -1,85 +0,0 @@
# nint — Nix INTerpreter
`nint` is a shebang compatible interpreter for nix. It is currently
implemented as a fairly trivial wrapper around `nix-instantiate --eval`.
It allows to run nix expressions as command line tools if they conform
to the following calling convention:
* Every nix script needs to evaluate to a function which takes an
attribute set as its single argument. Ideally a set pattern with
an ellipsis should be used. By default `nint` passes the following
arguments:
* `currentDir`: the current working directory as a nix path
* `argv`: a list of arguments to the invokation including the
program name at `builtins.head argv`.
* Extra arguments can be manually passed as described below.
* The return value should always be a string (throwing is also okay)
which is printed to stdout by `nint`.
## Usage
```
nint [ --arg ARG VALUE … ] script.nix [ ARGS … ]
```
Instead of `--arg`, `--argstr` can also be used. They both work
like the flags of the same name for `nix-instantiate` and may
be specified any number of times as long as they are passed
*before* the nix expression to run.
Below is a shebang which also passes `depot` as an argument
(note the usage of `env -S` to get around the shebang limitation
to two arguments).
```nix
#!/usr/bin/env -S nint --arg depot /path/to/depot
```
## Limitations
* No side effects except for writing to `stdout`.
* Output is not streaming, i. e. even if the output is incrementally
calculated, nothing will be printed until the full output is available.
With plain nix strings we can't do better anyways.
* Limited error handling for the script, no way to set the exit code etc.
Some of these limitations may be possible to address in the future by using
an alternative nix interpreter and a more elaborate calling convention.
## Example
Below is a (very simple) implementation of a `ls(1)`-like program in nix:
```nix
#!/usr/bin/env nint
{ currentDir, argv, ... }:
let
lib = import <nixpkgs/lib>;
dirs =
let
args = builtins.tail argv;
in
if args == []
then [ currentDir ]
else args;
makeAbsolute = p:
if builtins.isPath p
then p
else if builtins.match "^/.*" p != null
then p
else "${toString currentDir}/${p}";
in
lib.concatStringsSep "\n"
(lib.flatten
(builtins.map
(d: (builtins.attrNames (builtins.readDir (makeAbsolute d))))
dirs)) + "\n"
```

View file

@ -1,14 +0,0 @@
{ depot, pkgs, ... }:
let
inherit (depot.nix.writers)
rustSimpleBin
;
in
rustSimpleBin {
name = "nint";
dependencies = [
depot.third_party.rust-crates.serde_json
];
} (builtins.readFile ./nint.rs)

View file

@ -1,110 +0,0 @@
extern crate serde_json;
use serde_json::Value;
use std::ffi::OsString;
use std::os::unix::ffi::{OsStringExt, OsStrExt};
use std::io::{Error, ErrorKind, Write};
use std::process::Command;
fn render_nix_string(s: &OsString) -> OsString {
let mut rendered = Vec::new();
rendered.extend(b"\"");
for b in s.as_os_str().as_bytes() {
match char::from(*b) {
'\"' => rendered.extend(b"\\\""),
'\\' => rendered.extend(b"\\\\"),
'$' => rendered.extend(b"\\$"),
_ => rendered.push(*b),
}
}
rendered.extend(b"\"");
OsString::from_vec(rendered)
}
fn render_nix_list(arr: &[OsString]) -> OsString {
let mut rendered = Vec::new();
rendered.extend(b"[ ");
for el in arr {
rendered.extend(render_nix_string(el).as_os_str().as_bytes());
rendered.extend(b" ");
}
rendered.extend(b"]");
OsString::from_vec(rendered)
}
fn main() -> std::io::Result<()> {
let mut nix_args = Vec::new();
let mut args = std::env::args_os().into_iter();
let mut in_args = true;
let mut argv: Vec<OsString> = Vec::new();
// skip argv[0]
args.next();
loop {
let arg = match args.next() {
Some(a) => a,
None => break,
};
if !arg.to_str().map(|s| s.starts_with("-")).unwrap_or(false) {
in_args = false;
}
if in_args {
match(arg.to_str()) {
Some("--arg") | Some("--argstr") => {
nix_args.push(arg);
nix_args.push(args.next().unwrap());
nix_args.push(args.next().unwrap());
Ok(())
}
_ => Err(Error::new(ErrorKind::Other, "unknown argument")),
}?
} else {
argv.push(arg);
}
}
if argv.len() < 1 {
Err(Error::new(ErrorKind::Other, "missing argv"))
} else {
let cd = std::env::current_dir()?.into_os_string();
nix_args.push(OsString::from("--arg"));
nix_args.push(OsString::from("currentDir"));
nix_args.push(cd);
nix_args.push(OsString::from("--arg"));
nix_args.push(OsString::from("argv"));
nix_args.push(render_nix_list(&argv[..]));
nix_args.push(OsString::from("--eval"));
nix_args.push(OsString::from("--json"));
nix_args.push(argv[0].clone());
let run = Command::new("nix-instantiate")
.args(nix_args)
.output()?;
match serde_json::from_slice(&run.stdout[..]) {
Ok(Value::String(s)) => Ok(print!("{}", s)),
Ok(_) => Err(Error::new(ErrorKind::Other, "output must be a string")),
_ => {
std::io::stderr().write_all(&run.stderr[..]);
Err(Error::new(ErrorKind::Other, "internal nix error"))
},
}
}
}