From cbde891d1ced129f6284cbadade652182aa5d2fb Mon Sep 17 00:00:00 2001 From: Chris Wells Date: Wed, 14 Feb 2024 15:13:20 -0600 Subject: Fixing my earlier mistakes --- flake.nix | 22 +++--- modules/build.nix | 174 +++++++++++++++++++++++++++++++++++++++++++++++ modules/default.nix | 5 +- modules/file.nix | 41 +---------- modules/kfmon.nix | 21 +++--- modules/kobo.nix | 53 +++++---------- modules/koreader.nix | 28 ++++---- modules/lib/file.nix | 43 ++++++++++++ modules/lib/manifest.nix | 41 +++++++++++ modules/manifest.nix | 2 +- modules/nickel.nix | 25 ++++--- modules/plato.nix | 58 ++++++++-------- modules/root.nix | 21 ++++++ 13 files changed, 379 insertions(+), 155 deletions(-) create mode 100644 modules/build.nix create mode 100644 modules/lib/file.nix create mode 100644 modules/lib/manifest.nix create mode 100644 modules/root.nix diff --git a/flake.nix b/flake.nix index 0a165b9..a79a9e7 100644 --- a/flake.nix +++ b/flake.nix @@ -11,14 +11,6 @@ overlays.default = import ./overlays; - # koboConfigurations.default = self.lib.koboConfiguration { - # pkgs = import nixpkgs { - # system = "x86_64-linux"; - # overlays = builtins.attrValues self.overlays; - # }; - # modules = [ ./config ]; - # }; - } // flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { @@ -30,14 +22,16 @@ modules = [ ./config ]; }; in { + packages = { - default = myKobo.config.kobo.package; - installer = myKobo.config.installer.package; + default = myKobo.config.build.src; + # installer = myKobo.config.installer.package; }; - apps = { - type = "app"; - program = "${pkgs.lib.getExe myKobo.config.installer.package} ${myKobo.config.kobo.package}"; - }; + # apps = { + # type = "app"; + # program = "${pkgs.lib.getExe myKobo.config.installer.package} ${myKobo.config.kobo.src}"; + # }; + }); } diff --git a/modules/build.nix b/modules/build.nix new file mode 100644 index 0000000..dfff338 --- /dev/null +++ b/modules/build.nix @@ -0,0 +1,174 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + cfg = config.build; + + copyFileFunc = '' + function copyFile() { + local SOURCE="$1" + local RELTARGET="$2" + local EXECUTABLE="3" + + local TARGET + TARGET="$(realpath -m "$REALOUT/$RELTARGET")" + if [[ ! $TARGET == $REALOUT* ]]; then + echo "Error installing file $RELTARGET outside of kobo src" >&2 + exit 1 + fi + + mkdir -pv "$(dirname "$TARGET")" + if [[ -d "$SOURCE" ]]; then + if [[ -d "$TARGET" ]]; then + cp -rfv "$SOURCE/." "$TARGET/" + else + cp -rfv "$SOURCE" "$TARGET" + fi + else + cp -fv "$SOURCE" "$TARGET" + fi + + if [[ "$EXECUTABLE" == "inherit" ]]; then + : + elif [[ "$EXECUTABLE" ]]; then + chmod +x "$TARGET" + else + chmod -x "$TARGET" + fi + } + ''; + + copyFile = { source, executable, target, ... }: '' + copyFile ${escapeShellArgs [ + source + (if executable == null then "inherit" else toString executable) + ]} + ''; + + installFile = writeShellApplication { + name = "kobo-install-file"; + runtimeInputs = [ pkgs.coreutils ]; + text = '' + DRY_RUN_ARG="''${1-}" + LOCAL_DIR="''${2-}" + KOBO_DIR="''${3-}" + LOCAL_FILE="''${4-}" + + FILENAME="''${LOCAL_FILE/#$LOCAL_DIR}" + KOBO_FILE="$KOBO_DIR/$FILENAME" + + ${toShellVar "MANIFEST" (cfg.manifestLookupFor "$LOCAL_FILE" "$KOBO_FILE")} + + ACTION="''${MANIFEST["$FILENAME"]}" + + if [[ -n "$ACTION" ]]; then + $DRY_RUN_ARG eval "$ACTION" + elif [[ -d "$LOCAL_FILE" ]]; then + $DRY_RUN_ARG mkdir -p "$KOBO_FILE" + else + $DRY_RUN_ARG mkdir -p "$(dirname "$KOBO_FILE")" + $DRY_RUN_ARG cp -f "$LOCAL_FILE" "$KOBO_FILE" + fi + ''; + }; +in { + options.build = { + allFiles = mkOption { + readOnly = true; + internal = true; + default = with config; concatMap (_: attrValues) [ + file + kfmon.file + kobo.file + koreader.file + nickel.file + plato.file + ]; + apply = filter (v: v.enable); + }; + + allManifests = mkOption { + readOnly = true; + internal = true; + default = with config; concatMap (_: attrValues) [ + manifest + kfmon.manifest + kobo.manifest + koreader.manifest + nickel.manifest + plato.manifest + ]; + apply = filter (v: v.enable); + }; + + manifestLookup = mkOption { + readOnly = true; + internal = true; + type = with types; attrsOf (functionTo (functionTo str)); + default = + let + split = concatMap ({ targets, mergeTool, ... }: map (name: { inherit name; value = mergeTool; }) targets); + in listToAttrs split; + }; + + manifestLookupFor = mkOption { + readOnly = true; + internal = true; + type = with types; functionTo (functionTo (attrsOf str)); + default = x: y: mapAttrs (_: f: f x y) cfg.manifestLookup; + }; + + src = mkOption { + type = types.package; + readOnly = true; + internal = true; + default = pkgs.stdenv.mkDerivation { + inherit (config) src; + name = "kobo_src"; + phases = [ "installPhase" ]; + installPhase = '' + mkdir -pv $out + cp -rv $src/. $out/ + chmod -R +w $out + + REALOUT="$(realpath -m "$out")" + + ${copyFileFunc} + + ${concatStringsSep "\n" ( + mapAttrsToList (_: copyFile) cfg.allFiles + )} + ''; + }; + }; + + installer = mkOption { + type = types.package; + readOnly = true; + internal = true; + default = pkgs.writeShellApplication { + name = "kobo-installer"; + runtimeInputs = [ + pkgs.coreutils + pkgs.util-linux + ]; + text = '' #TODO DRY_RUN_ARG + KOBO_LABEL="''${1:-KOBOeReader}" + + KOBO_MOUNTPOINT="$(findmnt -nlo TARGET LABEL="$KOBO_LABEL")" + if [[ -z "$KOBO_MOUNTPOINT" ]]; then + echo "Couldn't find a Kobo eReader volume with label $KOBO_LABEL. Are you sure one is actually mounted?" + exit 255 + fi + + KOBO_DIR="$KOBO_MOUNTPOINT/.kobo" + if [[ ! -d "$KOBO_DIR" ]]; then + echo "It doesn't seem like $KOBO_MOUNTPOINT actually points to a Kobo eReader since it doesn't contain a .kobo directory . . . " + exit 255 + fi + + find "${cfg.src}/" -not -path "${cfg.src}/" -exec ${installFile}/bin/kobo-install-file "" "${cfg.src}" "$KOBO_DIR" {} \; + ''; + }; + }; + }; +} diff --git a/modules/default.nix b/modules/default.nix index 8e04cce..856ece0 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1,6 +1,9 @@ { imports = [ - ./installer.nix + # ./installer.nix + ./build.nix + ./file.nix + ./manifest.nix ./kfmon.nix ./kobo.nix ./koreader.nix diff --git a/modules/file.nix b/modules/file.nix index a52fb84..7bd2549 100644 --- a/modules/file.nix +++ b/modules/file.nix @@ -3,44 +3,5 @@ with builtins // lib; let cfg = config.file; in { - options.file = mkOption { - default = {}; - type = with types; lazyAttrsOf (submodule ({ name, config, ... }: { - options = { - enable = mkEnableOption name // { default = true; }; - target = mkOption { - type = types.str; - default = name; - }; - executable = mkOption { - type = with types; nullOr bool; - default = null; - }; - directory = mkOption { - type = types.bool; - default = false; - description = '' - Enable this if the source file is a directory. Note that directories are copied first when forming the source - ''; - }; - source = mkOption { - type = types.path; - }; - text = mkOption { - type = with types; nullOr lines; - default = null; - }; - }; - - config = mkMerge [ - (mkIf (config.text != null) { - directory = false; - source = pkgs.writeText name config.text; - }) - (mkIf (config.text == "") { - enable = false; - }) - ]; - })); - }; + options.file = (import ./lib/file.nix { inherit pkgs lib; } "").option; } diff --git a/modules/kfmon.nix b/modules/kfmon.nix index 772a10b..33f6a9c 100644 --- a/modules/kfmon.nix +++ b/modules/kfmon.nix @@ -2,6 +2,7 @@ with builtins // lib; let cfg = config.kfmon; + importOption = p: (import p { inherit pkgs lib; } cfg.path).option; in { options.kfmon = { enable = mkEnableOption "KFMon"; @@ -12,19 +13,13 @@ in { description = "Relative path to kfmon within src"; }; - file = mkOption { - type = options.file.type; - default = {}; - description = "Files relative to ${cfg.path}"; - }; + file = importOption ./lib/file.nix; + manifest = importOption ./lib/manifest.nix; + # file = mkOption { + # type = options.file.type; + # default = {}; + # description = "Files relative to ${cfg.path}"; + # }; }; - config = mkIf cfg.enable { - file = mapAttrs' (n: v: { - name = "kfmon-${n}"; - value = v // { - target = "${cfg.path}/${v.target}"; - }; - }) cfg.file - }; } diff --git a/modules/kobo.nix b/modules/kobo.nix index 229a5d1..57f7a88 100644 --- a/modules/kobo.nix +++ b/modules/kobo.nix @@ -3,6 +3,7 @@ with builtins // lib; let cfg = config.kobo; ini = pkgs.formats.ini {}; + importOption = p: (import p { inherit pkgs lib; } cfg.path).option; in { options.kobo = { enable = mkEnableOption "main configurations"; @@ -13,51 +14,30 @@ in { description = "Relative path to main Kobo config"; }; - file = mkOption { - type = options.file.type; - default = {}; - description = "Files relative to ${cfg.path}"; - }; - - manifest = mkOption { - type = options.manifest.type; - default = {}; - description = "Manifest relative to ${cfg.path}"; - }; + file = importOption ./lib/file.nix; + manifest = importOption ./lib/manifest.nix; config = mkOption { type = ini.type; default = {}; description = "https://wiki.mobileread.com/wiki/Kobo_Configuration_Options"; }; - - # src = mkOption { - # type = types.package; - # description = "The base source files to use"; - # default = with pkgs.kobo.src; - # if config.plato.enable && config.koreader.enable then KOReader-Plato - # else if config.plato.enable then Plato - # else if config.koreader.enable then KOReader - # else if config.kfmon.enable then KFMon - # else error "At least one of plato, koreader or kfmon must be enabled in order to use a default package"; - # }; - }; config = { - file = mapAttrs' (n: v: { - name = "kobo-${n}"; - value = v // { - target = "${cfg.path}/${v.target}"; - }; - }) cfg.file; + # file = mapAttrs' (n: v: { + # name = "kobo-${n}"; + # value = v // { + # target = "${cfg.path}/${v.target}"; + # }; + # }) cfg.file; - manifest = mapAttrs' (n: v: { - name = "kobo-${n}"; - value = v // { - targets = map (x: "${cfg.path}/${x}") v.targets; - }; - }) cfg.manifest; + # manifest = mapAttrs' (n: v: { + # name = "kobo-${n}"; + # value = v // { + # targets = map (x: "${cfg.path}/${x}") v.targets; + # }; + # }) cfg.manifest; kobo = { config = { @@ -82,4 +62,5 @@ in { }; - } + }; +} diff --git a/modules/koreader.nix b/modules/koreader.nix index 0a7300a..aec90fe 100644 --- a/modules/koreader.nix +++ b/modules/koreader.nix @@ -2,6 +2,7 @@ with builtins // lib; let cfg = config.koreader; + importOption = p: (import p { inherit pkgs lib; } cfg.path).option; in { options.koreader = { enable = mkEnableOption "KOReader"; @@ -14,22 +15,25 @@ in { nickelEntry = mkEnableOption "Launch KOReader directly via nickelMenu"; - file = mkOption { - type = options.file.type; - default = {}; - description = "Files relative to ${cfg.path}"; - }; + file = importOption ./lib/file.nix; + manifest = importOption ./lib/manifest.nix; + + # file = mkOption { + # type = options.file.type; + # default = {}; + # description = "Files relative to ${cfg.path}"; + # }; }; config = mkIf cfg.enable { - file = mapAttrs' (n: v: { - name = "koreader-${n}"; - value = v // { - target = "${cfg.path}/${v.target}"; - }; - }) cfg.file + # file = mapAttrs' (n: v: { + # name = "koreader-${n}"; + # value = v // { + # target = "${cfg.path}/${v.target}"; + # }; + # }) cfg.file; - nickel.menu.items = mkIf cfg.nickelEntry { + nickel.menu.item = mkIf cfg.nickelEntry { koreader = singleton { location = "main"; label = "KOReader"; diff --git a/modules/lib/file.nix b/modules/lib/file.nix new file mode 100644 index 0000000..4e7a595 --- /dev/null +++ b/modules/lib/file.nix @@ -0,0 +1,43 @@ +{ lib, pkgs, ... }: +with builtins // lib; + +basePath: +rec { + option = mkOption { + type = fileType; + default = {}; + description = "Files relative to ${basePath}"; + }; + + type = types.attrsOf (types.submodule ({ name, config, ... }: { + options = { + enable = mkEnableOption name // { default = text != ""; }; + target = mkOption { + type = types.str; + default = name; + apply = p: if basePath == "" || isPrefix basePath p then p else "${basePath}/${p}"; + }; + executable = mkOption { + type = with types; nullOr bool; + default = null; + }; + source = mkOption { + type = types.path; + }; + text = mkOption { + type = with types; nullOr lines; + default = null; + }; + }; + + config = mkMerge [ + (mkIf (config.text != null) { + source = mkDefault pkgs.writeTextFile { + inherit name; + inherit (config) text; + executable = config.executable == true; + }; + }) + ]; + })) +} diff --git a/modules/lib/manifest.nix b/modules/lib/manifest.nix new file mode 100644 index 0000000..1acb567 --- /dev/null +++ b/modules/lib/manifest.nix @@ -0,0 +1,41 @@ +{ pkgs, lib, ... }: +with builtins // lib; + +basePath: +rec { + option = mkOption { + type = manifestType; + default = {}; + description = "Manifest relative to ${basePath}"; + }; + type = types.attrsOf (types.submodule ({ name, config, ... }: { + options = { + enable = mkEnableOption name // { default = true; }; + targets = mkOption { + type = with types; listOf str; + default = [ name ]; + apply = map (p: if basePath = "" || isPrefix basePath p then p else "${basePath}/${p}"); + }; + mergeTool = mkOption { + type = with types; functionTo (functionTo str); + description = '' + Sometimes we don't just want to overwrite the existing kobo file; instead we need to merge them. + This is the executable which does this. + The first argument is the local file and the second is the kobo file. + Note that the local file path and the kobo file path only disagree in their top-level directory and otherwise are the same path. + ''; + example = '' + localFile: koboFile: "cp ''${localFile} ''${koboFile}" + ''; + }; + # remoteOnly = mkOption { + # type = types.bool; + # default = false; + # description = '' + # In general, the manifest only considers local files that match the target/pattern. + # Setting this to true means that the kobo files will be scanned and the provided mergeTool will be run with the first argument set to null. + # ''; + # }; + }; + })) +} diff --git a/modules/manifest.nix b/modules/manifest.nix index 718ffb4..30a2060 100644 --- a/modules/manifest.nix +++ b/modules/manifest.nix @@ -25,7 +25,7 @@ in { ''; }; remoteOnly = mkOption { - type = types.bool + type = types.bool; default = false; description = '' In general, the manifest only considers local files that match the target/pattern. diff --git a/modules/nickel.nix b/modules/nickel.nix index 948dbaf..4d22c70 100644 --- a/modules/nickel.nix +++ b/modules/nickel.nix @@ -2,6 +2,7 @@ with builtins // lib; let cfg = config.nickel; + importOption = p: (import p { inherit pkgs lib; } cfg.path).option; action = mkOption { type = types.enum [ "cmd_spawn" @@ -57,11 +58,13 @@ in { description = "Relative path to nickelMenu within src"; }; - file = mkOption { - type = options.file.type; - default = {}; - description = "Files relative to ${cfg.path}"; - }; + file = importOption ./lib/file.nix; + manifest = importOption ./lib/manifest.nix; + # file = mkOption { + # type = options.file.type; + # default = {}; + # description = "Files relative to ${cfg.path}"; + # }; menu = { item = mkOption { @@ -133,11 +136,11 @@ in { ]; }; - file = mapAttrs' (n: v: { - name = "nm-${n}"; - value = v // { - target = "${cfg.path}/${v.target}"; - }; - }) cfg.file + # file = mapAttrs' (n: v: { + # name = "nm-${n}"; + # value = v // { + # target = "${cfg.path}/${v.target}"; + # }; + # }) cfg.file; }; } diff --git a/modules/plato.nix b/modules/plato.nix index f274f00..2fb1016 100644 --- a/modules/plato.nix +++ b/modules/plato.nix @@ -3,13 +3,14 @@ with builtins // lib; let cfg = config.plato; toml = pkgs.formats.toml {}; + importOption = p: (import p { inherit pkgs lib; } cfg.path).option; in { imports = [ ./nickel.nix ]; options.plato = { - enable = mkEnableOpiton "Plato"; + enable = mkEnableOption "Plato"; path = mkOption { type = types.srt; @@ -17,17 +18,20 @@ in { description = "Relative path to Plato within src"; }; - file = mkOption { - type = options.file.type; - default = {}; - description = "Files relative to ${cfg.path}"; - }; + file = importOption ./lib/file.nix; + manifest = importOption ./lib/manifest.nix; - manifest = mkOption { - type = options.manifest.type; - default = {}; - description = "Manifest relative to ${cfg.path}"; - }; + # file = mkOption { + # type = options.file.type; + # default = {}; + # description = "Files relative to ${cfg.path}"; + # }; + + # manifest = mkOption { + # type = options.manifest.type; + # default = {}; + # description = "Manifest relative to ${cfg.path}"; + # }; nickelEntry = mkEnableOption "Launch Plato directly from nickelMenu"; @@ -138,7 +142,7 @@ in { name = if hasSuffix ".css" n1 then n1 else "{n1}.css"; in { inherit name; - value = { inherit text; } + value = { inherit text; }; }) cfg.css) (mapAttrs' (n: t: @@ -153,11 +157,11 @@ in { { dictionaries = { - directory = true; + # directory = true; source = cfg.allDictionaries; }; fonts = { - directory = true; + # directory = true; source = cfg.allFonts; }; } @@ -169,19 +173,19 @@ in { }; }; - file = mapAttrs' (n: v: { - name = "plato-${n}"; - value = v // { - target = "${cfg.path}/${v.target}"; - }; - }) cfg.file; - - manifest = mapAttrs' (n: v: { - name = "plato-${n}"; - value = v // { - targets = map (x: "${cfg.path}/${x}") v.targets; - }; - }) cfg.manifest; + # file = mapAttrs' (n: v: { + # name = "plato-${n}"; + # value = v // { + # target = "${cfg.path}/${v.target}"; + # }; + # }) cfg.file; + + # manifest = mapAttrs' (n: v: { + # name = "plato-${n}"; + # value = v // { + # targets = map (x: "${cfg.path}/${x}") v.targets; + # }; + # }) cfg.manifest; nickel.menu.item = mkIf cfg.nickelEntry { plato = singleton { diff --git a/modules/root.nix b/modules/root.nix new file mode 100644 index 0000000..d74fe66 --- /dev/null +++ b/modules/root.nix @@ -0,0 +1,21 @@ +{ config, pkgs, lib, ... }: +with builtins // lib; +let + importOption = p: (import p { inherit pkgs lib; } "").option; +in { + options = { + file = importOption ./lib/file.nix; + manifest = importOption ./lib/manifest.nix; + + src = mkOption { + type = types.package; + description = "The base source files to use"; + default = with pkgs.kobo.src; + if config.plato.enable && config.koreader.enable then KOReader-Plato + else if config.plato.enable then Plato + else if config.koreader.enable then KOReader + else if config.kfmon.enable then KFMon + else pkgs.runCommandLocal "empty-kobo" {} "mkdir -p $out"; + }; + }; +} -- cgit v1.2.3