Files
nix-custom-sqlite/src/core-init-ext.nix
T

115 lines
3.4 KiB
Nix

{
lib,
extensions,
nameSuffix,
version,
headers,
stdenv,
writeText,
...
}:
assert lib.assertMsg (extensions != [ ]) "This derivation should not be evaluated if there are no extensions to build.";
# TODO: assert no colliding init/shutdown symbols across extensions.
let
initSymbols = builtins.catAttrs "init" extensions;
shutdownSymbols = builtins.catAttrs "shutdown" extensions;
coreInitHeaders = writeText "sqlite-core-init.h" ''
int core_init(const char *dummy);
void core_shutdown(void);
${lib.concatMapStringsSep "\n" (sym: "int ${sym}(const char *dummy);") initSymbols}
${lib.concatMapStringsSep "\n" (sym: "void ${sym}(void);") shutdownSymbols}
'';
# TODO: how should shutdown be handled with use of sqlite3_auto_extension? Is there a separate place for registering these?
coreInitImpl = writeText "sqlite-core-init.c" ''
#include "${coreInitHeaders}"
#include <sqlite3.h>
int core_init(const char *dummy) {
${lib.concatMapStringsSep "\n" (sym: "sqlite3_auto_extension((void(*)(void))${sym});") initSymbols}
return SQLITE_OK;
}
void core_shutdown(void) {
${lib.concatMapStringsSep "\n" (sym: "${sym}();") shutdownSymbols}
}
'';
in stdenv.mkDerivation (finalAttrs: {
pname = "sqlite-core-init${nameSuffix}";
version = version;
src = null;
dontUnpack = true;
buildInputs = [ headers ];
buildPhase = ''
runHook preBuild
"$CC" -c '${coreInitImpl}' -o sqlite-core-init.o
runHook postBuild
'';
installPhase = ''
runHook preInstall
install -Dm644 '${coreInitHeaders}' "$out/include/sqlite-core-init.h"
mkdir -p "$out/lib"
"$AR" rcs "$out/lib/libsqlite-core-init.a" sqlite-core-init.o
runHook postInstall
'';
doCheck = true;
checkPhase = ''
runHook preCheck
for ext in ${lib.escapeShellArgs (builtins.catAttrs "library" extensions)}; do
if nm -g --defined-only "$ext" | grep -q " sqlite_api$"; then
echo "Extension $ext appears to reference the 'sqlite_api' symbol, which is not expected for extensions which are going to be statically linked."
echo "If your are building the extension yourself, please ensure that it is built with \`-DSQLITE_CORE\`"
exit 1
fi
done
declare -A expectedSymbols;
${lib.concatMapStringsSep "\n" (ext: "expectedSymbols[${lib.escapeShellArg ext.init}]=${lib.escapeShellArg ext.library};") extensions}
${lib.concatMapStringsSep "\n" (ext: "expectedSymbols[${lib.escapeShellArg ext.shutdown}]=${lib.escapeShellArg ext.library};") (lib.filter (ext: ext.shutdown or null != null) extensions)}
for sym in "''${!expectedSymbols[@]}"; do
if ! nm -g --defined-only "''${expectedSymbols[$sym]}" | grep -q " ''${sym}$"; then
echo "Expected symbol $sym not found in ''${expectedSymbols[$sym]}."
exit 1
fi
done
runHook postCheck
'';
passthru = {
extraLDFLAGS = [
"-L${finalAttrs.finalPackage}/lib"
"-lsqlite-core-init"
] ++ lib.concatMap (ext: [
"-L${lib.dirOf ext.library}"
"-l${lib.removeSuffix ".so" (lib.removeSuffix ".a" (lib.removePrefix "lib" (lib.baseNameOf ext.library)))}"
]) extensions;
extraMakeFlags = let
opts = [
"-DSQLITE_EXTRA_INIT=core_init"
"-DSQLITE_EXTRA_SHUTDOWN=core_shutdown"
"-DSQLITE_CUSTOM_INCLUDE=${placeholder "out"}/include/sqlite-core-init.h"
];
in map (opt: "OPTIONS+=${lib.escapeShellArg opt}") opts;
};
})