Code change:
<ins> { lib , stdenv , fetchFromGitHub , emptyDirectory , writeText , makeBinaryWrapper , gradle , jdk21 , llvmPackages }: let gradleInit = writeText "init.gradle" '' logger.lifecycle 'Replacing Maven repositories with empty directory...' gradle.projectsLoaded { rootProject.allprojects { buildscript { repositories { clear() maven { url '${emptyDirectory}' } } } repositories { clear() maven { url '${emptyDirectory}' } } } } settingsEvaluated { settings -> settings.pluginManagement { repositories { maven { url '${emptyDirectory}' } } } } ''; in stdenv.mkDerivation { pname = "jextract"; version = "unstable-2023-11-27"; src = fetchFromGitHub { owner = "openjdk"; repo = "jextract"; rev = "8730fcf05c229d035b0db52ee6bd82622e9d03e9"; # Update jextract 21 with latest fixes hash = "sha256-Wct/yx5C0EjDtDyXNYDH5LRmrfq7islXbPVIGBR6x5Y="; }; nativeBuildInputs = [ gradle makeBinaryWrapper ]; env = { ORG_GRADLE_PROJECT_llvm_home = llvmPackages.libclang.lib; ORG_GRADLE_PROJECT_jdk21_home = jdk21; }; buildPhase = '' runHook preBuild export GRADLE_USER_HOME=$(mktemp -d) gradle --console plain --init-script "${gradleInit}" assemble runHook postBuild ''; doCheck = true; checkPhase = '' runHook preCheck gradle --console plain --init-script "${gradleInit}" verify runHook postCheck ''; installPhase = '' runHook preInstall mkdir -p $out/opt/ cp -r ./build/jextract $out/opt/jextract makeBinaryWrapper "$out/opt/jextract/bin/jextract" "$out/bin/jextract" runHook postInstall ''; meta = with lib; { description = "A tool which mechanically generates Java bindings from a native library headers"; mainProgram = "jextract"; homepage = "https://github.com/openjdk/jextract"; platforms = jdk21.meta.platforms; license = licenses.gpl2Only; maintainers = with maintainers; [ sharzy ]; }; } </ins>

Related discussion:
text: And now that Panama/FF&M is final, I'm hoping it will be possible to use jextract 22 to build headers for JDK 23+. Newer versions of jextract might offer improvements, but headers generated for JDK 22 should work wth JDK 23+ (I don't know what guarantees the JDK makes for that, but this seems a reasonable conclusion given a stable API.)
Nice to hear that, and thanks for your work! IMO, I think it's worth. The point is that JDK 21 is a LTS version, some downstreams prefer to use a LTS version of JDK to develop their projects. For them, if we don't keep JDK 21's , this will be unavailable to them unless they give up on choosing LTS and do some extra effort - porting their project to a newer version of JDK.
I agree. But I also strongly encourage new projects to start with JDK 22 and existing projects to migrate as quickly as possible. Having to make sure "preview mode" is set in the compiler and at runtime can cause various problems with tools and the output of the latest is significantly improved. In general, it's much better to be using a non-LTS JDK with a non-preview feature than and LTS JDK with a preview feature. I migrated my project recently and wish I had migrated to JDK 22 much earlier.
PR has been merged. I gave it a quick test on macOS/aarch64 and it seems to be working. (I used it to build which uses JDK 22 and Panama via Gradle's Java toolchains)