dprint-plugin-exec icon indicating copy to clipboard operation
dprint-plugin-exec copied to clipboard

Offer statically linked executable

Open mknapik opened this issue 8 months ago • 2 comments

When running the plugin in NixOS:

> ./dprint-plugin-exec
Could not start dynamically linked executable: ./dprint
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box. For more information, see:
https://nix.dev/permalink/stub-ld

binary release gnu

> ldd dprint-plugin-exec-gnu
	linux-vdso.so.1 (0x00007f0c9e7db000)
	libgcc_s.so.1 => /nix/store/m2047a1xwgblgkrnbxz0yilkaqfrbf2b-xgcc-14-20241116-libgcc/lib/libgcc_s.so.1 (0x00007f0c9e7a7000)
	libpthread.so.0 => /nix/store/rmy663w9p7xb202rcln4jjzmvivznmz8-glibc-2.40-66/lib/libpthread.so.0 (0x00007f0c9e7a2000)
	libdl.so.2 => /nix/store/rmy663w9p7xb202rcln4jjzmvivznmz8-glibc-2.40-66/lib/libdl.so.2 (0x00007f0c9e79d000)
	libc.so.6 => /nix/store/rmy663w9p7xb202rcln4jjzmvivznmz8-glibc-2.40-66/lib/libc.so.6 (0x00007f0c9e000000)
	/lib64/ld-linux-x86-64.so.2 => /nix/store/rmy663w9p7xb202rcln4jjzmvivznmz8-glibc-2.40-66/lib64/ld-linux-x86-64.so.2 (0x00007f0c9e7dd000)

binary release musl (works)

> ldd dprint-plugin-exec-musl
	statically linked

build from source (works)

ldd ./target/release/dprint-plugin-exec
	linux-vdso.so.1 (0x00007fe5d643b000)
	libgcc_s.so.1 => /nix/store/ik84lbv5jvjm1xxvdl8mhg52ry3xycvm-gcc-14-20241116-lib/lib/libgcc_s.so.1 (0x00007fe5d61d2000)
	libc.so.6 => /nix/store/rmy663w9p7xb202rcln4jjzmvivznmz8-glibc-2.40-66/lib/libc.so.6 (0x00007fe5d5e00000)
	/nix/store/rmy663w9p7xb202rcln4jjzmvivznmz8-glibc-2.40-66/lib/ld-linux-x86-64.so.2 => /nix/store/rmy663w9p7xb202rcln4jjzmvivznmz8-glibc-2.40-66/lib64/ld-linux-x86-64.so.2 (0x00007fe5d643d000)

I'm aware NixOS breaks compatibility with FHS.

Would it be feasible to provide binaries which are statically linked?

mknapik avatar Apr 08 '25 19:04 mknapik

Or, is there a way to use musl instead of glibc, when configuring dprint in dprint.json?

mknapik avatar Apr 08 '25 20:04 mknapik

Early proof-of-concept of dealing with directly in nix via an overlay, but statically linked would be nice to not have to do this.

I essentially provide an output of a sharable config so all the hashing (config -> plugin.json -> zip) can be done directly in nix so it can be used like below. Not the cleanest, but did get it to work. If we can't get a static binary (and if I can find the time), I can maybe setup a repo with the build artifacts that auto updates on a schedule or via renovate.

If anyone else has the time to champion, feel free to use any of below if it helps.

  "extends": [
    "/nix/store/r69gr5pgz7dlgw6wc22x2qb72578r48r-dprint-plugin-exec/share/dprint/configs/nix-dprint-plugin-exec-config.json",
  ],

Magic to make the binary work is using patchelf.

{lib, ...}: final: _: let
  rpath = final.lib.makeLibraryPath (with final; [
    stdenv.cc.cc.lib
    glibc
  ]);
  systemForDownload =
    {
      "aarch64-darwin" = "aarch64-apple-darwin";
      "aarch64-linux" = "aarch64-unknown-linux-gnu";
      "x86_64-darwin" = "x86_64-apple-darwin";
      "x86_64-linux" = "x86_64-unknown-linux-gnu";
    }
    .${final.system};
  systemForJson =
    {
      "aarch64-darwin" = "darwin-aarch64";
      "aarch64-linux" = "linux-aarch64";
      "x86_64-darwin" = "darwin-x86_64";
      "x86_64-linux" = "linux-x86_64";
    }
    .${final.system};
  version = "0.5.1";
  src = final.fetchzip {
    url = "https://github.com/dprint/dprint-plugin-exec/releases/download/${version}/dprint-plugin-exec-${systemForDownload}.zip";
    sha256 =
      {
        "aarch64-darwin" = lib.fakeHash;
        "aarch64-linux" = lib.fakeHash;
        "x86_64-darwin" = lib.fakeHash;
        "x86_64-linux" = "sha256-vclZSLs9TzAk4R9eE0Y9HJIBPe2sHsOj8shC7dMJ8XI=";
      }
      .${final.system};
  };

  dprint-plugin-exec-zip = final.stdenv.mkDerivation {
    name = "dprint-plugin-exec-zip";
    inherit src version;

    phases = ["installPhase"];

    installPhase = ''
      mkdir -p $out
      cp $src/dprint-plugin-exec $out/dprint-plugin-exec
      chmod +wx $out/dprint-plugin-exec

      ${
        if final.stdenv.hostPlatform.isLinux
        then ''
          patchelf \
            --set-interpreter $(cat $NIX_CC/nix-support/dynamic-linker) \
            --set-rpath ${rpath} \
            $out/dprint-plugin-exec
        ''
        else ""
      }

      cd $out
      ${lib.getExe final.zip} dprint-plugin-exec.zip dprint-plugin-exec
      rm dprint-plugin-exec
    '';
  };

  dprint-plugin-exec-json = final.writeTextFile {
    name = "plugin.json";
    text = builtins.toJSON {
      schemaVersion = 2;
      kind = "process";
      name = "dprint-plugin-exec";
      inherit version;
      "${systemForJson}" = {
        reference = "file://${dprint-plugin-exec-zip}/dprint-plugin-exec.zip";
        checksum = builtins.hashFile "sha256" "${dprint-plugin-exec-zip}/dprint-plugin-exec.zip";
      };
    };
  };

  dprint-plugin-exec-config = final.writeTextFile {
    name = "dprint.json";
    text = builtins.toJSON {
      plugins = [
        "${dprint-plugin-exec-json}@${builtins.hashFile "sha256" "${dprint-plugin-exec-json}"}"
      ];
    };
  };
in {
  # nix path-info .#dprint-plugin-exec
  dprint-plugin-exec = final.stdenvNoCC.mkDerivation {
    name = "dprint-plugin-exec";
    inherit version;

    phases = ["installPhase"];

    installPhase = ''
      mkdir -p $out/share/dprint/plugins/dprint-plugin-exec $out/share/dprint/configs
      ln -s ${dprint-plugin-exec-json} $out/share/dprint/plugins/dprint-plugin-exec/plugin.json
      ln -s ${dprint-plugin-exec-zip}/dprint-plugin-exec.zip $out/share/dprint/plugins/dprint-plugin-exec/dprint-plugin-exec.zip
      ln -s ${dprint-plugin-exec-config} $out/share/dprint/configs/nix-dprint-plugin-exec-config.json
    '';
  };
}

higherorderfunctor avatar Apr 10 '25 11:04 higherorderfunctor