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
|
The project is still in its early stages and some important
|
||||||
restrictions should be highlighted:
|
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
|
* Only SBCL is supported (though the plan is to add support for at
|
||||||
least ABCL and Clozure CL, and maybe make it extensible)
|
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 |
|
| `srcs` | `list<path>` | List of paths to source files | yes |
|
||||||
| `deps` | `list<drv>` | List of dependencies | no |
|
| `deps` | `list<drv>` | List of dependencies | no |
|
||||||
| `native` | `list<drv>` | List of native 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
|
The output of invoking this is a directory containing a FASL file
|
||||||
that is the concatenated result of all compiled sources.
|
that is the concatenated result of all compiled sources.
|
||||||
|
@ -46,6 +45,7 @@ restrictions should be highlighted:
|
||||||
| `deps` | `list<drv>` | List of dependencies | no |
|
| `deps` | `list<drv>` | List of dependencies | no |
|
||||||
| `native` | `list<drv>` | List of native dependencies | no |
|
| `native` | `list<drv>` | List of native dependencies | no |
|
||||||
| `main` | `string` | Entrypoint function | 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
|
The `main` parameter should be the name of a function and defaults
|
||||||
to `${name}:main` (i.e. the *exported* `main` function of the
|
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
|
pre-loaded with all of that Lisp code and can be used as the host
|
||||||
for e.g. Sly or SLIME.
|
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
|
## Example
|
||||||
|
|
||||||
Using buildLisp could look like this:
|
Using buildLisp could look like this:
|
||||||
|
@ -92,5 +108,10 @@ in buildLisp.program {
|
||||||
name = "example";
|
name = "example";
|
||||||
deps = [ libExample ];
|
deps = [ libExample ];
|
||||||
srcs = [ ./main.lisp ];
|
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' determines whether Lisp library 'b' depends on 'a'.
|
||||||
dependsOn = a: b: builtins.elem a b.lispDeps;
|
dependsOn = a: b: builtins.elem a b.lispDeps;
|
||||||
|
|
||||||
|
@ -103,64 +117,121 @@ let
|
||||||
overrideLisp = new: makeOverridable f (orig // (new orig));
|
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
|
# Public API functions
|
||||||
#
|
#
|
||||||
|
|
||||||
# 'library' builds a list of Common Lisp files into a single FASL
|
# 'library' builds a list of Common Lisp files into a single FASL
|
||||||
# which can then be loaded into SBCL.
|
# which can then be loaded into SBCL.
|
||||||
library = { name, srcs, deps ? [], native ? [] }:
|
library =
|
||||||
let
|
{ name
|
||||||
lispNativeDeps = (allNative native deps);
|
, srcs
|
||||||
lispDeps = allDeps deps;
|
, deps ? []
|
||||||
in runCommandNoCC "${name}-cllib" {
|
, native ? []
|
||||||
LD_LIBRARY_PATH = lib.makeLibraryPath lispNativeDeps;
|
, tests ? null
|
||||||
LANG = "C.UTF-8";
|
}:
|
||||||
} ''
|
let
|
||||||
${sbcl}/bin/sbcl --script ${genCompileLisp srcs lispDeps}
|
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
|
# FASL files can be combined by simply concatenating them
|
||||||
# together, but it needs to be in the compilation order.
|
# together, but it needs to be in the compilation order.
|
||||||
mkdir $out
|
mkdir $out
|
||||||
|
|
||||||
chmod +x cat_fasls
|
chmod +x cat_fasls
|
||||||
./cat_fasls > $out/${name}.fasl
|
./cat_fasls > $out/${name}.fasl
|
||||||
'' // {
|
'' // {
|
||||||
inherit lispNativeDeps lispDeps;
|
inherit lispNativeDeps lispDeps;
|
||||||
lispName = name;
|
lispName = name;
|
||||||
lispBinary = false;
|
lispBinary = false;
|
||||||
};
|
tests = testDrv;
|
||||||
|
};
|
||||||
|
|
||||||
# 'program' creates an executable containing a dumped image of the
|
# 'program' creates an executable containing a dumped image of the
|
||||||
# specified sources and dependencies.
|
# specified sources and dependencies.
|
||||||
program = { name, main ? "${name}:main", srcs, deps ? [], native ? [] }:
|
program =
|
||||||
let
|
{ name
|
||||||
lispDeps = allDeps deps;
|
, main ? "${name}:main"
|
||||||
libPath = lib.makeLibraryPath (allNative native lispDeps);
|
, srcs
|
||||||
selfLib = library {
|
, deps ? []
|
||||||
inherit name srcs native;
|
, native ? []
|
||||||
deps = lispDeps;
|
, 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
|
# 'bundled' creates a "library" that calls 'require' on a built-in
|
||||||
# package, such as any of SBCL's sb-* packages.
|
# package, such as any of SBCL's sb-* packages.
|
||||||
|
|
Loading…
Reference in a new issue