zmk/release.nix
Chris Andreae c7fc3432cd
Docker container and lambda function for performing firmware builds
Provides an entry point that builds and returns a combined LH + RH keyboard
firmware when provided a keymap via a POST body.

Wraps compilation with ccache, and includes a pre-warmed cache of the build in /tmp/ccache.
To maximize chance of a direct cache hit, changes the lambda driver to always build in /tmp/build.

some back of the envelope measurements (2012 xeon e3-1230v2, nixos)
clean build, no cache -> 21.308
clean build, cache -> 7.145
modified keymap, clean build, cache -> 12.127
2022-12-24 13:00:14 +09:00

153 lines
4.2 KiB
Nix

{ pkgs ? (import ./nix/pinned-nixpkgs.nix {}), revision ? "HEAD" }:
let
lib = pkgs.lib;
zmkPkgs = (import ./default.nix { inherit pkgs; });
lambda = (import ./lambda { inherit pkgs; });
ccacheWrapper = pkgs.callPackage ./nix/ccache.nix {};
nix-utils = pkgs.fetchFromGitHub {
owner = "iknow";
repo = "nix-utils";
rev = "c13c7a23836c8705452f051d19fc4dff05533b53";
sha256 = "0ax7hld5jf132ksdasp80z34dlv75ir0ringzjs15mimrkw8zcac";
};
ociTools = pkgs.callPackage "${nix-utils}/oci" {};
inherit (zmkPkgs) zmk zephyr;
accounts = {
users.deploy = {
uid = 999;
group = "deploy";
home = "/home/deploy";
shell = "/bin/sh";
};
groups.deploy.gid = 999;
};
baseLayer = {
name = "base-layer";
path = [ pkgs.busybox ];
entries = ociTools.makeFilesystem {
inherit accounts;
tmp = true;
usrBinEnv = "${pkgs.busybox}/bin/env";
binSh = "${pkgs.busybox}/bin/sh";
};
};
depsLayer = {
name = "deps-layer";
path = [ pkgs.ccache ];
includes = zmk.buildInputs ++ zmk.nativeBuildInputs ++ zmk.zephyrModuleDeps;
};
zmkCompileScript = let
zmk' = zmk.override {
gcc-arm-embedded = ccacheWrapper.override {
unwrappedCC = pkgs.gcc-arm-embedded;
};
};
zmk_glove80_rh = zmk.override { board = "glove80_rh"; };
realpath_coreutils = if pkgs.stdenv.isDarwin then pkgs.coreutils else pkgs.busybox;
in pkgs.writeShellScriptBin "compileZmk" ''
set -eo pipefail
if [ ! -f "$1" ]; then
echo "Usage: compileZmk [file.keymap]" >&2
exit 1
fi
KEYMAP="$(${realpath_coreutils}/bin/realpath $1)"
export PATH=${lib.makeBinPath (with pkgs; zmk'.nativeBuildInputs ++ [ ccache ])}:$PATH
export CMAKE_PREFIX_PATH=${zephyr}
export CCACHE_BASEDIR=$PWD
export CCACHE_NOHASHDIR=t
export CCACHE_COMPILERCHECK=none
if [ -n "$DEBUG" ]; then ccache -z; fi
cmake -G Ninja -S ${zmk'.src}/app ${lib.escapeShellArgs zmk'.cmakeFlags} "-DUSER_CACHE_DIR=/tmp/.cache" "-DKEYMAP_FILE=$KEYMAP" -DBOARD=glove80_lh
ninja
if [ -n "$DEBUG" ]; then ccache -s; fi
cat zephyr/zmk.uf2 ${zmk_glove80_rh}/zmk.uf2 > zephyr/combined.uf2
'';
ccacheCache = pkgs.runCommandNoCC "ccache-cache" {
nativeBuildInputs = [ zmkCompileScript ];
} ''
export CCACHE_DIR=$out
mkdir /tmp/build
cd /tmp/build
compileZmk ${zmk.src}/app/boards/arm/glove80/glove80.keymap
'';
entrypoint = pkgs.writeShellScriptBin "entrypoint" ''
set -euo pipefail
if [ ! -d "$CCACHE_DIR" ]; then
cp -r ${ccacheCache} "$CCACHE_DIR"
chmod -R u=rwX,go=u-w "$CCACHE_DIR"
fi
if [ ! -d /tmp/build ]; then
mkdir /tmp/build
fi
exec "$@"
'';
startLambda = handler: pkgs.writeShellScriptBin "startLambda" ''
set -euo pipefail
export PATH=${lib.makeBinPath [ zmkCompileScript ]}:$PATH
cd ${lambda.source}
${lambda.bundleEnv}/bin/bundle exec aws_lambda_ric "app.LambdaFunction::${handler}.process"
'';
simulateLambda = lambda: pkgs.writeShellScriptBin "simulateLambda" ''
${pkgs.aws-lambda-rie}/bin/aws-lambda-rie ${lambda}/bin/startLambda
'';
lambdaImage = lambda:
let
appLayer = {
name = "app-layer";
path = [ lambda zmkCompileScript ];
};
in
ociTools.makeSimpleImage {
name = "zmk-builder-lambda";
layers = [ baseLayer depsLayer appLayer ];
config = {
User = "deploy";
WorkingDir = "/tmp";
Entrypoint = [ "${entrypoint}/bin/entrypoint" ];
Cmd = [ "startLambda" ];
Env = [ "CCACHE_DIR=/tmp/ccache" "REVISION=${revision}" ];
};
};
# There are two lambda handler functions, depending on whether the lambda is
# expected to handle Api Gateway/ELB HTTP requests itself.
startHttpLambda = startLambda "HttpHandler";
startDirectLambda = startLambda "DirectHandler";
httpLambdaImage = lambdaImage startHttpLambda;
directLambdaImage = lambdaImage startDirectLambda;
simulateDirectLambda = simulateLambda startDirectLambda;
simulateHttpLambda = simulateLambda startHttpLambda;
in {
inherit httpLambdaImage directLambdaImage zmkCompileScript ccacheCache;
# nix shell -f release.nix simulateDirectLambda -c simulateLambda
inherit simulateHttpLambda simulateDirectLambda;
}