feat(nix/buildLisp): Add abstraction for test suites
Add support for explicitly specifying tests as part of a buildLisp program or library. Change-Id: I733213c1618f0fa60f645465560bce0522641efd Reviewed-on: https://cl.tvl.fyi/c/depot/+/1481 Tested-by: BuildkiteCI Reviewed-by: tazjin <mail@tazj.in>
This commit is contained in:
parent
d98f2ea68f
commit
3089f6b6ce
2 changed files with 139 additions and 47 deletions
|
@ -16,8 +16,6 @@ Nix-based ecosystem. This offers several advantages over ASDF:
|
|||
The project is still in its early stages and some important
|
||||
restrictions should be highlighted:
|
||||
|
||||
* There is no separate abstraction for tests at the moment (i.e. they
|
||||
are built and run as programs)
|
||||
* Only SBCL is supported (though the plan is to add support for at
|
||||
least ABCL and Clozure CL, and maybe make it extensible)
|
||||
|
||||
|
@ -33,6 +31,7 @@ restrictions should be highlighted:
|
|||
| `srcs` | `list<path>` | List of paths to source files | yes |
|
||||
| `deps` | `list<drv>` | List of dependencies | no |
|
||||
| `native` | `list<drv>` | List of native dependencies | no |
|
||||
| `test` | see "Tests" | Specification for test suite | no |
|
||||
|
||||
The output of invoking this is a directory containing a FASL file
|
||||
that is the concatenated result of all compiled sources.
|
||||
|
@ -46,6 +45,7 @@ restrictions should be highlighted:
|
|||
| `deps` | `list<drv>` | List of dependencies | no |
|
||||
| `native` | `list<drv>` | List of native dependencies | no |
|
||||
| `main` | `string` | Entrypoint function | no |
|
||||
| `test` | see "Tests" | Specification for test suite | no |
|
||||
|
||||
The `main` parameter should be the name of a function and defaults
|
||||
to `${name}:main` (i.e. the *exported* `main` function of the
|
||||
|
@ -71,6 +71,22 @@ restrictions should be highlighted:
|
|||
pre-loaded with all of that Lisp code and can be used as the host
|
||||
for e.g. Sly or SLIME.
|
||||
|
||||
## Tests
|
||||
|
||||
Both `buildLisp.library` and `buildLisp.program` take an optional argument
|
||||
`tests`, which has the following supported fields:
|
||||
|
||||
| parameter | type | use | required? |
|
||||
|--------------|--------------|-------------------------------|-----------|
|
||||
| `name` | `string` | Name of the test suite | no |
|
||||
| `expression` | `string` | Lisp expression to run tests | yes |
|
||||
| `srcs` | `list<path>` | List of paths to source files | no |
|
||||
| `native` | `list<drv>` | List of native dependencies | no |
|
||||
|
||||
the `expression` parameter should be a Lisp expression and will be evaluated
|
||||
after loading all sources and dependencies (including library/program
|
||||
dependencies). It must return a non-`NIL` value if the test suite has passed.
|
||||
|
||||
## Example
|
||||
|
||||
Using buildLisp could look like this:
|
||||
|
@ -92,5 +108,10 @@ in buildLisp.program {
|
|||
name = "example";
|
||||
deps = [ libExample ];
|
||||
srcs = [ ./main.lisp ];
|
||||
tests = {
|
||||
deps = [ lispPkgs.fiveam ];
|
||||
srcs = [ ./tests.lisp ];
|
||||
expression = "(fiveam:run!)";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
|
|
@ -60,6 +60,20 @@ let
|
|||
))
|
||||
'';
|
||||
|
||||
# 'genTestLisp' generates a Lisp file that loads all sources and deps and
|
||||
# executes expression
|
||||
genTestLisp = name: srcs: deps: expression: writeText "${name}.lisp" ''
|
||||
;; Dependencies
|
||||
${genLoadLisp deps}
|
||||
|
||||
;; Sources
|
||||
${lib.concatStringsSep "\n" (map (src: "(load \"${src}\")") srcs)}
|
||||
|
||||
;; Test expression
|
||||
(unless ${expression}
|
||||
(exit :code 1))
|
||||
'';
|
||||
|
||||
# 'dependsOn' determines whether Lisp library 'b' depends on 'a'.
|
||||
dependsOn = a: b: builtins.elem a b.lispDeps;
|
||||
|
||||
|
@ -103,64 +117,121 @@ let
|
|||
overrideLisp = new: makeOverridable f (orig // (new orig));
|
||||
};
|
||||
|
||||
# 'testSuite' builds a Common Lisp test suite that loads all of srcs and deps,
|
||||
# and then executes expression to check its result
|
||||
testSuite = { name, expression, srcs, deps ? [], native ? [] }:
|
||||
let
|
||||
lispNativeDeps = allNative native deps;
|
||||
lispDeps = allDeps deps;
|
||||
in runCommandNoCC name {
|
||||
LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
|
||||
LANG = "C.UTF-8";
|
||||
} ''
|
||||
echo "Running test suite ${name}"
|
||||
|
||||
${sbcl}/bin/sbcl --script ${genTestLisp name srcs deps expression} \
|
||||
| tee $out
|
||||
|
||||
echo "Test suite ${name} succeeded"
|
||||
'';
|
||||
|
||||
#
|
||||
# Public API functions
|
||||
#
|
||||
|
||||
# 'library' builds a list of Common Lisp files into a single FASL
|
||||
# which can then be loaded into SBCL.
|
||||
library = { name, srcs, deps ? [], native ? [] }:
|
||||
let
|
||||
lispNativeDeps = (allNative native deps);
|
||||
lispDeps = allDeps deps;
|
||||
in runCommandNoCC "${name}-cllib" {
|
||||
LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
|
||||
LANG = "C.UTF-8";
|
||||
} ''
|
||||
${sbcl}/bin/sbcl --script ${genCompileLisp srcs lispDeps}
|
||||
library =
|
||||
{ name
|
||||
, srcs
|
||||
, deps ? []
|
||||
, native ? []
|
||||
, tests ? null
|
||||
}:
|
||||
let
|
||||
lispNativeDeps = (allNative native deps);
|
||||
lispDeps = allDeps deps;
|
||||
testDrv = if ! isNull tests
|
||||
then testSuite {
|
||||
name = tests.name or "${name}-test";
|
||||
srcs = srcs ++ (tests.srcs or []);
|
||||
deps = deps ++ (tests.deps or []);
|
||||
expression = tests.expression;
|
||||
}
|
||||
else null;
|
||||
in runCommandNoCC "${name}-cllib" {
|
||||
LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
|
||||
LANG = "C.UTF-8";
|
||||
} ''
|
||||
${if ! isNull testDrv
|
||||
then "echo 'Test ${testDrv} succeeded'"
|
||||
else "echo 'No tests run'"}
|
||||
${sbcl}/bin/sbcl --script ${genCompileLisp srcs lispDeps}
|
||||
|
||||
echo "Compilation finished, assembling FASL files"
|
||||
echo "Compilation finished, assembling FASL files"
|
||||
|
||||
# FASL files can be combined by simply concatenating them
|
||||
# together, but it needs to be in the compilation order.
|
||||
mkdir $out
|
||||
# FASL files can be combined by simply concatenating them
|
||||
# together, but it needs to be in the compilation order.
|
||||
mkdir $out
|
||||
|
||||
chmod +x cat_fasls
|
||||
./cat_fasls > $out/${name}.fasl
|
||||
'' // {
|
||||
inherit lispNativeDeps lispDeps;
|
||||
lispName = name;
|
||||
lispBinary = false;
|
||||
};
|
||||
chmod +x cat_fasls
|
||||
./cat_fasls > $out/${name}.fasl
|
||||
'' // {
|
||||
inherit lispNativeDeps lispDeps;
|
||||
lispName = name;
|
||||
lispBinary = false;
|
||||
tests = testDrv;
|
||||
};
|
||||
|
||||
# 'program' creates an executable containing a dumped image of the
|
||||
# specified sources and dependencies.
|
||||
program = { name, main ? "${name}:main", srcs, deps ? [], native ? [] }:
|
||||
let
|
||||
lispDeps = allDeps deps;
|
||||
libPath = lib.makeLibraryPath (allNative native lispDeps);
|
||||
selfLib = library {
|
||||
inherit name srcs native;
|
||||
deps = lispDeps;
|
||||
program =
|
||||
{ name
|
||||
, main ? "${name}:main"
|
||||
, srcs
|
||||
, deps ? []
|
||||
, native ? []
|
||||
, tests ? null
|
||||
}:
|
||||
let
|
||||
lispDeps = allDeps deps;
|
||||
libPath = lib.makeLibraryPath (allNative native lispDeps);
|
||||
selfLib = library {
|
||||
inherit name srcs native;
|
||||
deps = lispDeps;
|
||||
};
|
||||
testDrv = if ! isNull tests
|
||||
then testSuite {
|
||||
name = tests.name or "${name}-test";
|
||||
srcs =
|
||||
(
|
||||
srcs ++ (tests.srcs or []));
|
||||
deps = deps ++ (tests.deps or []);
|
||||
expression = tests.expression;
|
||||
}
|
||||
else null;
|
||||
in runCommandNoCC "${name}" {
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
LD_LIBRARY_PATH = libPath;
|
||||
LANG = "C.UTF-8";
|
||||
} ''
|
||||
${if ! isNull testDrv
|
||||
then "echo 'Test ${testDrv} succeeded'"
|
||||
else ""}
|
||||
mkdir -p $out/bin
|
||||
|
||||
${sbcl}/bin/sbcl --script ${
|
||||
genDumpLisp name main ([ selfLib ] ++ lispDeps)
|
||||
}
|
||||
|
||||
wrapProgram $out/bin/${name} --prefix LD_LIBRARY_PATH : "${libPath}"
|
||||
'' // {
|
||||
lispName = name;
|
||||
lispDeps = [ selfLib ] ++ (tests.deps or []);
|
||||
lispNativeDeps = native;
|
||||
lispBinary = true;
|
||||
tests = testDrv;
|
||||
};
|
||||
in runCommandNoCC "${name}" {
|
||||
nativeBuildInputs = [ makeWrapper ];
|
||||
LD_LIBRARY_PATH = libPath;
|
||||
LANG = "C.UTF-8";
|
||||
} ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
${sbcl}/bin/sbcl --script ${
|
||||
genDumpLisp name main ([ selfLib ] ++ lispDeps)
|
||||
}
|
||||
|
||||
wrapProgram $out/bin/${name} --prefix LD_LIBRARY_PATH : "${libPath}"
|
||||
'' // {
|
||||
lispName = name;
|
||||
lispDeps = [ selfLib ];
|
||||
lispNativeDeps = native;
|
||||
lispBinary = true;
|
||||
};
|
||||
|
||||
# 'bundled' creates a "library" that calls 'require' on a built-in
|
||||
# package, such as any of SBCL's sb-* packages.
|
||||
|
|
Loading…
Reference in a new issue