Help to package a unfree java app

Hi all

I am trying to package a proprietary app named luniistore (https://lunii.com/en-us/). It is a companion app of a device for children.

They provide a deb and rpm package for Linux system. Those packages contains a packaged java app that seems to embed a JVM so the deb package contains jars and shared libraries.

I tried to package it and to use autopatchelf to make it work on nix however it ended up with a non functional binary. Patching the libraries of the embedded JVM seems to break it.

Here is the derivation:

{ lib
, stdenv
, pkgs
, fetchurl
, wrapGAppsHook
, dpkg
, autoPatchelfHook
, xorg
, gdk-pixbuf
, glib
, glibc
, gtk3
, gnome2
, ffmpeg
}:
stdenv.mkDerivation rec {
  pname = "luniistore";
  version = "2.2.261";

  src = fetchurl {
    url = "https://storage.googleapis.com/storage.lunii.fr/public/deploy/installers/linux/64bits/luniistore-${version}-64bits.deb";
    name = "${pname}.deb";
    sha256 = "sha256-DhtExcinNNVio2EhH+N/1/Fc/X57nLNsx+Oqm0gPrB4=";
  };

  nativeBuildInputs = [
    wrapGAppsHook
    autoPatchelfHook
  ];

  buildInputs = [
    dpkg
    xorg.libX11
    xorg.libxcb
    xorg.libXScrnSaver
    xorg.libXcomposite
    xorg.libXcursor
    xorg.libXdamage
    xorg.libXext
    xorg.libXfixes
    xorg.libXi
    xorg.libXrandr
    xorg.libXrender
    xorg.libXtst
    stdenv.cc.cc.lib
    stdenv.cc.cc
    gdk-pixbuf
    glib
    glibc
    gtk3
    gnome2.gtk
    ffmpeg

  ];

  dontBuild = true;
  dontConfigure = true;

  unpackPhase = ''
     mkdir pkg
     dpkg-deb -x $src pkg
     rm -rf pkg/opt/Luniistore/runtime/lib/amd64/libavplugin-53.so \
      pkg/opt/Luniistore/runtime/lib/amd64/libavplugin-54.so \
      pkg/opt/Luniistore/runtime/lib/amd64/libavplugin-55.so \
      pkg/opt/Luniistore/runtime/lib/amd64/libavplugin-56.so \
      pkg/opt/Luniistore/runtime/lib/amd64/libavplugin-57.so \
      pkg/opt/Luniistore/runtime/lib/amd64/libavplugin-ffmpeg-56.so \
      pkg/opt/Luniistore/runtime/lib/amd64/libavplugin-ffmpeg-57.so

     sourceRoot=pkg
  '';

  installPhase = ''
    runHook preInstall
    mkdir -p $out/bin $out/share/applications
    mv opt $out
    ln -s $out/opt/Luniistore/Luniistore $out/bin/Lunistore
    ln -s $out/opt/Luniistore/Luniistore.desktop $out/share/applications

    runHook postInstall
  '';

  meta = with lib; {
    description = "Luniistore";
    homepage = "https://lunii.com/";
    sourceProvenance = with sourceTypes; [ binaryNativeCode ];
    maintainers = with maintainers; [ aacebedo ];
    platforms = platforms.x86_64;
  };
}

I also tried to run the jar directly by install the default jre and launching the jar with
java --add-exports javafx.graphics/com.sun.javafx.application=ALL-UNNAMED --add-exports javafx.graphics/com.sun.javafx.util=ALL-UNNAMED -jar ./app/LauncherLoader-2.0.0.jar

It starts but then crashes with a java.lang.NoSuchMethodError exception:

Exception in thread "JavaFX Application Thread" java.lang.NoSuchMethodError: 'sun.util.logging.PlatformLogger com.sun.javafx.util.Logging.getCSSLogger()'
        at com.lunii.luniistore.ui.App.start(App.java:104)
        at com.lunii.luniistore.launcher.ui.dialogs.LauncherControl.lambda$launchAppFromManifest$13(LauncherControl.java:501)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:484)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:457)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:399)
        at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:456)
        at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
        at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method)
        at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:290)
        at java.base/java.lang.Thread.run(Thread.java:833)

It seems symbols located in the jfxrt.jar package is not found, I suspect a problem in java fx version but I was unable to solve it for the moment.

Any idea there ?

It’s unlikely that a patched jre will work. Try a different version of the jre/jdk. Nixpkgs has a number of them.

1 Like

In case you’d like to know how the jfx packaging works: https://github.com/NixOS/nixpkgs/blob/cf034a867e08b7e083df1658c915c58456dbbde2/pkgs/development/compilers/openjdk/17.nix#L7

I tried to install package oraclejdk without success. The nix package requires to download the tar.gz manually and to add it to the store. I used the instruction it gave me but it didn’t work.

I also tried to install the openjdk package by overriding the enableJavaFX option to be sure it is set to true. The jar refuses to start with the same NoSuchMethodError error.

I recently packaged a videogame that’s distributed similarly, with a built-in jvm and a bunch of jar files. I tried for quite a while to get it properly patchelf’d, or to use a nix-native version of the jvm, but ultimately failed at both approaches and just used buildFHSUserEnv. It’s ugly, but it gets the job done.

Can you share a link to your package please?

I don’t really have it online anywhere atm, so I’ll just paste it:

{ buildFHSUserEnv, fetchurl, stdenv, unzip, writeShellScript }:

let
  gamedir = stdenv.mkDerivation (self: {
    pname = "starsector";
    version = "0.95.1a-RC6";
    sha256 = "sha256-PaiL1RmKHDWrVX/xGXPE5vokPBOTah+vGjhn14/+/ZM=";

    src = fetchurl {
      url = "https://s3.amazonaws.com/fractalsoftworks/starsector/starsector_linux-${self.version}.zip";
      inherit (self) sha256;
    };

    nativeBuildInputs = [ unzip ];

    installPhase = ''
      mkdir -p $out
      cp -a * $out/
      substituteInPlace $out/starsector.sh \
        --replace ./jre_linux/bin/java 'exec ./jre_linux/bin/java -Djava.util.prefs.userRoot="$configdir"' \
        --replace -Dcom.fs.starfarer.settings.paths.saves=./saves             '-Dcom.fs.starfarer.settings.paths.saves="$configdir"/saves' \
        --replace -Dcom.fs.starfarer.settings.paths.screenshots=./screenshots '-Dcom.fs.starfarer.settings.paths.screenshots="$configdir"/sceenshots' \
        --replace -Dcom.fs.starfarer.settings.paths.logs=.                    '-Dcom.fs.starfarer.settings.paths.logs="$configdir"' \
        --replace -Dcom.fs.starfarer.settings.paths.mods=./mods               '-Dcom.fs.starfarer.settings.paths.mods="$modsdir"'
    '';
  });
in

buildFHSUserEnv {
  name = "starsector";
  targetPkgs = pkgs: builtins.attrValues {
    inherit (pkgs)
      alsa-lib
      gtk2
      libGL
      libxslt
    ;
    inherit (pkgs.xorg)
      libX11
      libXext
      libXrandr
      libXrender
      libXcursor
      libXi
      libXxf86vm
      libXtst
    ;
  };
  runScript = writeShellScript "starsector" ''
    cd ${gamedir}
    configdir="''${STARSECTOR_CONFIG_DIR:-$HOME/.config/starsector}"
    modsdir="''${STARSECTOR_MODS_DIR:-./mods}"
    unset STARSECTOR_CONFIG_DIR STARSECTOR_MODS_DIR
    mkdir -p "$configdir"/{saves,screenshots}
    source ./starsector.sh
  '';
}
1 Like

Thanks.
I’ve changed my package but still failing to launch with:

Luniistore Error invoking method.
Luniistore Failed to launch JVM

Here is the package:

{ lib
, stdenv
, pkgs
, fetchurl
, wrapGAppsHook
, dpkg
, xorg
, gdk-pixbuf
, glib
, glibc
, gtk3
, gnome2
, ffmpeg
, buildFHSUserEnv
, writeShellScript
}:
let
  pname = "luniistore";
  version = "2.2.261";

  meta = with lib; {
    description = "Luniistore";
    homepage = "https://lunii.com/";
    sourceProvenance = with sourceTypes; [ binaryNativeCode ];
    maintainers = with maintainers; [ aacebedo ];
    platforms = platforms.x86_64;
  };

  luniistore = stdenv.mkDerivation (self: {
    inherit pname version meta;

    src = fetchurl {
      url = "https://storage.googleapis.com/storage.lunii.fr/public/deploy/installers/linux/64bits/luniistore-${version}-64bits.deb";
      name = "${pname}.deb";
      sha256 = "sha256-DhtExcinNNVio2EhH+N/1/Fc/X57nLNsx+Oqm0gPrB4=";
    };
    nativeBuildInputs = [
      dpkg
    ];
    dontBuild = true;
    dontConfigure = true;


    unpackPhase = ''
      mkdir pkg
      dpkg-deb -x $src pkg

      sourceRoot=pkg
    '';

    installPhase = ''
      runHook preInstall

      mkdir -p $out/bin $out/share/applications
      mv opt $out
      ln -s $out/opt/Luniistore/Luniistore $out/bin/Lunistore
      ln -s $out/opt/Luniistore/Luniistore.desktop $out/share/applications

      runHook postInstall

    '';
  });
in
buildFHSUserEnv {
  inherit meta;
  name = pname;

  targetPkgs = pkgs: [
    luniistore
    xorg.libX11
    xorg.libxcb
    xorg.libXScrnSaver
    xorg.libXcomposite
    xorg.libXcursor
    xorg.libXdamage
    xorg.libXext
    xorg.libXfixes
    xorg.libXi
    xorg.libXrandr
    xorg.libXrender
    xorg.libXtst
    stdenv.cc.cc.lib
    stdenv.cc.cc
    ffmpeg
  ];

  runScript = writeShellScript "luniistore" ''
    cd ${luniistore}
    ./opt/Luniistore/Luniistore
  '';
}

I worked on a package which came with a bundled JRE. I removed said JRE and used one from Nixpkgs instead. It was challenging to pry apart the JIMAGEs to separate the JRE from the app, but in the end it worked.