From a3ea1bc599f5c7dcea950b40c05f1abd3e5cd191 Mon Sep 17 00:00:00 2001 From: ivanbrennan Date: Tue, 18 Jan 2022 00:04:15 -0500 Subject: [PATCH 1/3] nixos/xmonad: enableConfiguredRecompile Commit 9a5b5d9fe858f33f7f5ce0870be2b8a38516a1d4 added Haskell dependencies (GHC and packages) to the xmonad binary's environment even if xmonad had been preconfigured (via the "config" option). The intent was to enable one-off recompiling using a local config file (e.g. ~/.config/xmonad/xmonad.hs), so the user can get quick feedback while developing their config. While this works, it may not be a common use-case, and it requires some careful crafting in xmonad.hs itself. On top of that, it significantly increases the size of the closure. Given all that, commit b69d9d3c23311ce8b8512a7032600a5f8d1595ca removed GHC and packages from the binary's environment. But there are still those among us who want to be able to recompile from a preconfigured xmonad, so let's provide a way to opt-into configured recompilation. --- .../services/x11/window-managers/xmonad.nix | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix index ecad411ff683..4ac11541beaa 100644 --- a/nixos/modules/services/x11/window-managers/xmonad.nix +++ b/nixos/modules/services/x11/window-managers/xmonad.nix @@ -2,7 +2,7 @@ with lib; let - inherit (lib) mkOption mkIf optionals literalExpression; + inherit (lib) mkOption mkIf optionals literalExpression optionalString; cfg = config.services.xserver.windowManager.xmonad; ghcWithPackages = cfg.haskellPackages.ghcWithPackages; @@ -26,11 +26,14 @@ let in pkgs.runCommandLocal "xmonad" { nativeBuildInputs = [ pkgs.makeWrapper ]; - } '' + } ('' install -D ${xmonadEnv}/share/man/man1/xmonad.1.gz $out/share/man/man1/xmonad.1.gz makeWrapper ${configured}/bin/xmonad $out/bin/xmonad \ + '' + optionalString cfg.enableConfiguredRecompile '' + --set NIX_GHC "${xmonadEnv}/bin/ghc" \ + '' + '' --set XMONAD_XMESSAGE "${pkgs.xorg.xmessage}/bin/xmessage" - ''; + ''); xmonad = if (cfg.config != null) then xmonad-config else xmonad-vanilla; in { @@ -95,12 +98,14 @@ in { xmonad from PATH. This allows e.g. switching to the new xmonad binary after rebuilding your system with nixos-rebuild. For the same reason, ghc is not added to the environment when this - option is set. + option is set, unless is + set to true. If you actually want to run xmonad with a config specified here, but also be able to recompile and restart it from a copy of that source in - $HOME/.xmonad on the fly, you will have to implement that yourself - using something like "compileRestart" from the example. + $HOME/.xmonad on the fly, set + to true and implement something like "compileRestart" + from the example. This should allow you to switch at will between the local xmonad and the one NixOS puts in your PATH. ''; @@ -135,6 +140,16 @@ in { ''; }; + enableConfiguredRecompile = mkOption { + default = false; + type = lib.types.bool; + description = '' + Enable recompilation even if is set to a + non-null value. This adds the necessary Haskell dependencies (GHC with + packages) to the xmonad binary's environment. + ''; + }; + xmonadCliArgs = mkOption { default = []; type = with lib.types; listOf str; From 6c72deb51b13235adb4bd13ebba95e907f839e15 Mon Sep 17 00:00:00 2001 From: ivanbrennan Date: Tue, 18 Jan 2022 00:19:31 -0500 Subject: [PATCH 2/3] nixos/xmonad: update example config Update the example config to show a working example for xmonad 0.17.0, which added an argument to the `launch` function and adjusted the location of the recompiled binary. --- .../services/x11/window-managers/xmonad.nix | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/nixos/modules/services/x11/window-managers/xmonad.nix b/nixos/modules/services/x11/window-managers/xmonad.nix index 4ac11541beaa..68f97c2f504b 100644 --- a/nixos/modules/services/x11/window-managers/xmonad.nix +++ b/nixos/modules/services/x11/window-managers/xmonad.nix @@ -121,6 +121,29 @@ in { compiledConfig = printf "xmonad-%s-%s" arch os + myConfig = defaultConfig + { modMask = mod4Mask -- Use Super instead of Alt + , terminal = "urxvt" } + `additionalKeys` + [ ( (mod4Mask,xK_r), compileRestart True) + , ( (mod4Mask,xK_q), restart "xmonad" True ) ] + + -------------------------------------------- + {- version 0.17.0 -} + -------------------------------------------- + -- compileRestart resume = + -- dirs <- io getDirectories + -- whenX (recompile dirs True) $ + -- when resume writeStateToFile + -- *> catchIO + -- ( do + -- args <- getArgs + -- executeFile (cacheDir dirs compiledConfig) False args Nothing + -- ) + -- + -- main = getDirectories >>= launch myConfig + -------------------------------------------- + compileRestart resume = whenX (recompile True) $ when resume writeStateToFile @@ -131,12 +154,7 @@ in { executeFile (dir compiledConfig) False args Nothing ) - main = launch defaultConfig - { modMask = mod4Mask -- Use Super instead of Alt - , terminal = "urxvt" } - `additionalKeys` - [ ( (mod4Mask,xK_r), compileRestart True) - , ( (mod4Mask,xK_q), restart "xmonad" True ) ] + main = launch myConfig ''; }; From 44af29e6f5d8ca7d27ff951a52615ae238c27ccb Mon Sep 17 00:00:00 2001 From: ivanbrennan Date: Thu, 20 Jan 2022 01:48:05 -0500 Subject: [PATCH 3/3] nixosTests.xmonad: test configured recompilation Add test coverage for the enableConfiguredRecompile option, checking that we can compile and exec a new xmonad from a user's local config, as well as restart the originally configured xmonad. As I needed a reliable way to wait for recompilation to finish before proceeding with subsequent test steps, I adjusted the startup behavior to write a file ("oldXMonad" or "newXMonad") to /etc upon startup, and replaced some "sleep" calls with "wait_for_file". --- nixos/tests/xmonad.nix | 100 ++++++++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 15 deletions(-) diff --git a/nixos/tests/xmonad.nix b/nixos/tests/xmonad.nix index 078cd2118107..a14d4b819eb6 100644 --- a/nixos/tests/xmonad.nix +++ b/nixos/tests/xmonad.nix @@ -1,4 +1,55 @@ -import ./make-test-python.nix ({ pkgs, ...} : { +import ./make-test-python.nix ({ pkgs, ...}: + +let + mkConfig = name: keys: '' + import XMonad + import XMonad.Operations (restart) + import XMonad.Util.EZConfig + import XMonad.Util.SessionStart + import Control.Monad (when) + import Text.Printf (printf) + import System.Posix.Process (executeFile) + import System.Info (arch,os) + import System.Environment (getArgs) + import System.FilePath (()) + + main = launch $ def { startupHook = startup } `additionalKeysP` myKeys + + startup = isSessionStart >>= \sessInit -> + spawn "touch /tmp/${name}" + >> if sessInit then setSessionStarted else spawn "xterm" + + myKeys = [${builtins.concatStringsSep ", " keys}] + + compiledConfig = printf "xmonad-%s-%s" arch os + + compileRestart resume = + whenX (recompile True) $ + when resume writeStateToFile + *> catchIO + ( do + dir <- getXMonadDataDir + args <- getArgs + executeFile (dir compiledConfig) False args Nothing + ) + ''; + + oldKeys = + [ ''("M-C-x", spawn "xterm")'' + ''("M-q", restart "xmonad" True)'' + ''("M-C-q", compileRestart True)'' + ''("M-C-t", spawn "touch /tmp/somefile")'' # create somefile + ]; + + newKeys = + [ ''("M-C-x", spawn "xterm")'' + ''("M-q", restart "xmonad" True)'' + ''("M-C-q", compileRestart True)'' + ''("M-C-r", spawn "rm /tmp/somefile")'' # delete somefile + ]; + + newConfig = pkgs.writeText "xmonad.hs" (mkConfig "newXMonad" newKeys); +in { name = "xmonad"; meta = with pkgs.lib.maintainers; { maintainers = [ nequissimus ]; @@ -10,21 +61,10 @@ import ./make-test-python.nix ({ pkgs, ...} : { services.xserver.displayManager.defaultSession = "none+xmonad"; services.xserver.windowManager.xmonad = { enable = true; + enableConfiguredRecompile = true; enableContribAndExtras = true; extraPackages = with pkgs.haskellPackages; haskellPackages: [ xmobar ]; - config = '' - import XMonad - import XMonad.Operations (restart) - import XMonad.Util.EZConfig - import XMonad.Util.SessionStart - - main = launch $ def { startupHook = startup } `additionalKeysP` myKeys - - startup = isSessionStart >>= \sessInit -> - if sessInit then setSessionStarted else spawn "xterm" - - myKeys = [ ("M-C-x", spawn "xterm"), ("M-q", restart "xmonad" True) ] - ''; + config = mkConfig "oldXMonad" oldKeys; }; }; @@ -38,10 +78,40 @@ import ./make-test-python.nix ({ pkgs, ...} : { machine.wait_for_window("${user.name}.*machine") machine.sleep(1) machine.screenshot("terminal1") + machine.succeed("rm /tmp/oldXMonad") machine.send_key("alt-q") - machine.sleep(3) + machine.wait_for_file("/tmp/oldXMonad") machine.wait_for_window("${user.name}.*machine") machine.sleep(1) machine.screenshot("terminal2") + + # /tmp/somefile should not exist yet + machine.fail("stat /tmp/somefile") + + # original config has a keybinding that creates somefile + machine.send_key("alt-ctrl-t") + machine.sleep(1) + machine.succeed("stat /tmp/somefile") + + # set up the new config + machine.succeed("mkdir -p ${user.home}/.xmonad") + machine.copy_from_host("${newConfig}", "${user.home}/.xmonad/xmonad.hs") + + # recompile xmonad using the new config + machine.send_key("alt-ctrl-q") + machine.wait_for_file("/tmp/newXMonad") + + # new config has a keybinding that deletes somefile + machine.send_key("alt-ctrl-r") + machine.sleep(1) + machine.fail("stat /tmp/somefile") + + # restart with the old config, and confirm the old keybinding is back + machine.succeed("rm /tmp/oldXMonad") + machine.send_key("alt-q") + machine.wait_for_file("/tmp/oldXMonad") + machine.send_key("alt-ctrl-t") + machine.sleep(1) + machine.succeed("stat /tmp/somefile") ''; })