From 8a2843e0493132fd2a9eec20ab3c57fff64a8025 Mon Sep 17 00:00:00 2001 From: Sam <30577766+Samasaur1@users.noreply.github.com> Date: Sat, 29 Jun 2024 15:12:53 -0700 Subject: [PATCH 1/5] programs.jdks: init I've left stubs in place to allow setting the JDK in use, but I have not yet implemented that, so this only adds support for `programs.jdk.installed` --- modules/module-list.nix | 1 + modules/programs/jdks.nix | 98 +++++++++++++++++++++++++++ modules/system/activation-scripts.nix | 1 + modules/system/default.nix | 3 + 4 files changed, 103 insertions(+) create mode 100644 modules/programs/jdks.nix diff --git a/modules/module-list.nix b/modules/module-list.nix index 0b62158a..d438cbf0 100644 --- a/modules/module-list.nix +++ b/modules/module-list.nix @@ -93,6 +93,7 @@ ./programs/direnv.nix ./programs/fish.nix ./programs/gnupg.nix + ./programs/jdks.nix ./programs/man.nix ./programs/info ./programs/nix-index diff --git a/modules/programs/jdks.nix b/modules/programs/jdks.nix new file mode 100644 index 00000000..c94bc435 --- /dev/null +++ b/modules/programs/jdks.nix @@ -0,0 +1,98 @@ +{ config, lib, pkgs, ... }: + +let + cfg = config.programs.jdks; +in + +{ + meta.maintainers = [ + lib.maintainers.samasaur or "samasaur" + ]; + + options.programs.jdks = { + installed = lib.mkOption { + type = with lib.types; listOf package; + default = []; + example = lib.literalExpression + '' + [ pkgs.zulu8 pkgs.zulu11 pkgs.zulu17 ]; + ''; + description = '' + JDKs to install systemwide. + + These JDKs will be symlinked into /Library/Java/JavaVirtualMachines + ''; + }; + + # selected = lib.mkOption { + # type = with lib.types; nullOr package; # use string instead? or version? + # default = null; + # example = ""; + # description = '' + # The JDK to set as active. + # + # Tools such as `java`, `javac`, etc. will use this JDK. + # + # Should it also be added to the list of installed JDKs if not there? + # ''; + # }; + }; + + config = { + # environment.variables = lib.mkIf (cfg.selected != null) { + # JAVA_HOME = "hello"; + # }; + # launchd.envVariables = lib.mkIf (cfg.selected != null) { + # JAVA_HOME = "hello"; + # }; + + system.build.jdks = pkgs.runCommand "jdks" + { preferLocalBuild = true; } + '' + mkdir -p $out/Library/Java/JavaVirtualMachines + cd $out/Library/Java/JavaVirtualMachines + ${lib.concatMapStringsSep "\n" (jdk: ''ln -s "${jdk}/"*.jdk .'') cfg.installed} + ''; + + system.activationScripts.jdks.text = '' + echo "linking JDKs..." >&2 + + # link new JDKs + ${lib.optionalString (cfg.installed != []) '' + for _jdk in ${config.system.build.jdks}/Library/Java/JavaVirtualMachines/*.jdk; do + # $_jdk is the full nix store path (because it reads symlinks?), so shorten it (to e.g. `zulu-8.jdk`) + export jdk=$(basename $_jdk) + if ! diff "${config.system.build.jdks}/Library/Java/JavaVirtualMachines/''${jdk}" "/Library/Java/JavaVirtualMachines/''${jdk}" &> /dev/null; then + # $jdk from the new system is different from the one with the same name in /Library/Java/JavaVirtualMachines + if ! test -e "/Library/Java/JavaVirtualMachines/''${jdk}"; then + # one with the same name as $jdk doesn't exist in /Library/Java/JavaVirtualMachines + # do nothing + true + elif test -f "/Library/Java/JavaVirtualMachines/''${jdk}"; then + echo "Preexisting JDK /Library/Java/JavaVirtualMachines/$jdk was manually installed; not overwriting..." >&2 + continue + elif test -L "/Library/Java/JavaVirtualMachines/''${jdk}"; then + echo "Preexisting JDK /Library/Java/JavaVirtualMachines/$jdk was manually linked into place; overwriting..." >&2 + fi + # link $jdk into place, overwriting if necessary + ln -sf "${config.system.build.jdks}/Library/Java/JavaVirtualMachines/''${jdk}" "/Library/Java/JavaVirtualMachines/''${jdk}" + fi + done + ''} + + # remove installed JDKs that were from the previous system but aren't in the new system + for _jdk in $(ls /run/current-system/Library/Java/JavaVirtualMachines 2> /dev/null); do + # this is not actually necessary, but I'm doing it for consistency + export jdk=$(basename $_jdk) + if test ! -e "${config.system.build.jdks}/Library/Java/JavaVirtualMachines/''${jdk}"; then + # $jdk was in the old system config, but not the new system config + if test -e "/Library/Java/JavaVirtualMachines/''${jdk}"; then + # $jdk was linked into place; remove it + echo "Removing old JDK ''${jdk}..." >&2 + rm -f "/Library/Java/JavaVirtualMachines/''${jdk}" + fi + fi + done + ''; + }; +} diff --git a/modules/system/activation-scripts.nix b/modules/system/activation-scripts.nix index 83251998..af9b871d 100644 --- a/modules/system/activation-scripts.nix +++ b/modules/system/activation-scripts.nix @@ -70,6 +70,7 @@ in ${cfg.activationScripts.keyboard.text} ${cfg.activationScripts.fonts.text} ${cfg.activationScripts.nvram.text} + ${cfg.activationScripts.jdks.text} ${cfg.activationScripts.postActivation.text} diff --git a/modules/system/default.nix b/modules/system/default.nix index 285936c5..c1f2a490 100644 --- a/modules/system/default.nix +++ b/modules/system/default.nix @@ -123,6 +123,9 @@ in mkdir -p $out/user/Library ln -s ${cfg.build.launchd}/user/Library/LaunchAgents $out/user/Library/LaunchAgents + mkdir -p $out/Library/Java + ln -s ${cfg.build.jdks}/Library/Java/JavaVirtualMachines $out/Library/Java/JavaVirtualMachines + echo "$activationScript" > $out/activate substituteInPlace $out/activate --subst-var out chmod u+x $out/activate From 1110f3b4f9e08eb4efccc46e03b2391829c1b721 Mon Sep 17 00:00:00 2001 From: Sam <30577766+Samasaur1@users.noreply.github.com> Date: Tue, 2 Jul 2024 22:19:22 -0700 Subject: [PATCH 2/5] programs.jdks.selected: init --- modules/programs/jdks.nix | 49 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/modules/programs/jdks.nix b/modules/programs/jdks.nix index c94bc435..13c36e99 100644 --- a/modules/programs/jdks.nix +++ b/modules/programs/jdks.nix @@ -24,28 +24,21 @@ in ''; }; - # selected = lib.mkOption { - # type = with lib.types; nullOr package; # use string instead? or version? - # default = null; - # example = ""; - # description = '' - # The JDK to set as active. - # - # Tools such as `java`, `javac`, etc. will use this JDK. - # - # Should it also be added to the list of installed JDKs if not there? - # ''; - # }; + selected = lib.mkOption { + type = with lib.types; nullOr package; + default = null; + example = ""; + description = '' + The JDK to set as active. + + Tools such as `java`, `javac`, etc. will use this JDK. + + Should it also be added to the list of installed JDKs if not there? + ''; + }; }; config = { - # environment.variables = lib.mkIf (cfg.selected != null) { - # JAVA_HOME = "hello"; - # }; - # launchd.envVariables = lib.mkIf (cfg.selected != null) { - # JAVA_HOME = "hello"; - # }; - system.build.jdks = pkgs.runCommand "jdks" { preferLocalBuild = true; } '' @@ -93,6 +86,24 @@ in fi fi done + + # if a JDK is selected, `launchctl setenv` it + ${lib.optionalString (cfg.selected != null) '' + # prevent variables from propagating + ( + # set locale to C for stable sorting + export LC_ALL=C + for jdk in ${cfg.selected}/*.jdk; do + export JAVA_HOME="''${jdk}/Contents/Home" + # ensure that this JDK is actually a macOS JDK bundle before using it + if test -e "$JAVA_HOME"; then + launchctl setenv JAVA_HOME "$JAVA_HOME" + # break out of the loop so that we only set JAVA_HOME once, even if there are multiple JDKs in this derivation + break + fi + done + ) + ''} ''; }; } From 97a89348c38111c75232339ab3b114c3e110ea24 Mon Sep 17 00:00:00 2001 From: Sam <30577766+Samasaur1@users.noreply.github.com> Date: Tue, 2 Jul 2024 22:32:01 -0700 Subject: [PATCH 3/5] Mangle JDK names with nix store paths to prevent collisions --- modules/programs/jdks.nix | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/programs/jdks.nix b/modules/programs/jdks.nix index 13c36e99..892a6811 100644 --- a/modules/programs/jdks.nix +++ b/modules/programs/jdks.nix @@ -44,7 +44,11 @@ in '' mkdir -p $out/Library/Java/JavaVirtualMachines cd $out/Library/Java/JavaVirtualMachines - ${lib.concatMapStringsSep "\n" (jdk: ''ln -s "${jdk}/"*.jdk .'') cfg.installed} + ${lib.concatMapStringsSep "\n" (jdk: '' + for jdk in "${jdk}/*.jdk"; do + ln -s "''${jdk}" "./''${jdk//\//_}" + done + '') cfg.installed} ''; system.activationScripts.jdks.text = '' From c0470488b544dd7cffd426dc73f533d7fef72c0a Mon Sep 17 00:00:00 2001 From: Sam <30577766+Samasaur1@users.noreply.github.com> Date: Tue, 2 Jul 2024 23:24:19 -0700 Subject: [PATCH 4/5] Fix shell quoting I thought I understood bash quoting. I do not. Here's an example: ``` [sam](~)$ for jdk in "/nix/store/fq62102p6snl4mix9w0wpzw9a91ck4l1-zulu-ca-jdk-8.0.402/*.jdk"; do echo $jdk; echo ${jdk//\//_}; done /nix/store/fq62102p6snl4mix9w0wpzw9a91ck4l1-zulu-ca-jdk-8.0.402/zulu-8.jdk _nix_store_fq62102p6snl4mix9w0wpzw9a91ck4l1-zulu-ca-jdk-8.0.402_*.jdk [sam](~)$ for jdk in /nix/store/fq62102p6snl4mix9w0wpzw9a91ck4l1-zulu-ca-jdk-8.0.402/*.jdk; do echo $jdk; echo ${jdk//\//_}; done /nix/store/fq62102p6snl4mix9w0wpzw9a91ck4l1-zulu-ca-jdk-8.0.402/zulu-8.jdk _nix_store_fq62102p6snl4mix9w0wpzw9a91ck4l1-zulu-ca-jdk-8.0.402_zulu-8.jdk ``` --- modules/programs/jdks.nix | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/programs/jdks.nix b/modules/programs/jdks.nix index 892a6811..2ff878cc 100644 --- a/modules/programs/jdks.nix +++ b/modules/programs/jdks.nix @@ -45,7 +45,7 @@ in mkdir -p $out/Library/Java/JavaVirtualMachines cd $out/Library/Java/JavaVirtualMachines ${lib.concatMapStringsSep "\n" (jdk: '' - for jdk in "${jdk}/*.jdk"; do + for jdk in ${jdk}/*.jdk; do ln -s "''${jdk}" "./''${jdk//\//_}" done '') cfg.installed} @@ -58,7 +58,7 @@ in ${lib.optionalString (cfg.installed != []) '' for _jdk in ${config.system.build.jdks}/Library/Java/JavaVirtualMachines/*.jdk; do # $_jdk is the full nix store path (because it reads symlinks?), so shorten it (to e.g. `zulu-8.jdk`) - export jdk=$(basename $_jdk) + export jdk=$(basename "$_jdk") if ! diff "${config.system.build.jdks}/Library/Java/JavaVirtualMachines/''${jdk}" "/Library/Java/JavaVirtualMachines/''${jdk}" &> /dev/null; then # $jdk from the new system is different from the one with the same name in /Library/Java/JavaVirtualMachines if ! test -e "/Library/Java/JavaVirtualMachines/''${jdk}"; then @@ -80,7 +80,7 @@ in # remove installed JDKs that were from the previous system but aren't in the new system for _jdk in $(ls /run/current-system/Library/Java/JavaVirtualMachines 2> /dev/null); do # this is not actually necessary, but I'm doing it for consistency - export jdk=$(basename $_jdk) + export jdk=$(basename "$_jdk") if test ! -e "${config.system.build.jdks}/Library/Java/JavaVirtualMachines/''${jdk}"; then # $jdk was in the old system config, but not the new system config if test -e "/Library/Java/JavaVirtualMachines/''${jdk}"; then From 4402fd9f469502d43a673ef2f30f8fa9ed303618 Mon Sep 17 00:00:00 2001 From: Sam <30577766+Samasaur1@users.noreply.github.com> Date: Sat, 6 Jul 2024 17:43:12 -0700 Subject: [PATCH 5/5] Set selected JDK in LaunchAgent --- modules/programs/jdks.nix | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/modules/programs/jdks.nix b/modules/programs/jdks.nix index 2ff878cc..0e9fc5f9 100644 --- a/modules/programs/jdks.nix +++ b/modules/programs/jdks.nix @@ -27,13 +27,16 @@ in selected = lib.mkOption { type = with lib.types; nullOr package; default = null; - example = ""; + example = lib.literalExpression + '' + pkgs.zulu11 + ''; description = '' The JDK to set as active. Tools such as `java`, `javac`, etc. will use this JDK. - Should it also be added to the list of installed JDKs if not there? + It will not be added to the list of installed JDKs. ''; }; }; @@ -90,24 +93,25 @@ in fi fi done - - # if a JDK is selected, `launchctl setenv` it - ${lib.optionalString (cfg.selected != null) '' - # prevent variables from propagating - ( - # set locale to C for stable sorting - export LC_ALL=C - for jdk in ${cfg.selected}/*.jdk; do - export JAVA_HOME="''${jdk}/Contents/Home" - # ensure that this JDK is actually a macOS JDK bundle before using it - if test -e "$JAVA_HOME"; then - launchctl setenv JAVA_HOME "$JAVA_HOME" - # break out of the loop so that we only set JAVA_HOME once, even if there are multiple JDKs in this derivation - break - fi - done - ) - ''} ''; + + launchd.agents.java_home = lib.mkIf (cfg.selected != null) { + script = '' + export LC_ALL=C + for jdk in ${cfg.selected}/*.jdk; do + export JAVA_HOME="''${jdk}/Contents/Home" + # ensure that this JDK is actually a macOS JDK bundle before using it + if test -e "$JAVA_HOME"; then + launchctl setenv JAVA_HOME "$JAVA_HOME" + # break out of the loop so that we only set JAVA_HOME once, even if there are multiple JDKs in this derivation + break + fi + done + ''; + + serviceConfig = { + RunAtLoad = true; + }; + }; }; }