summaryrefslogtreecommitdiffstats
path: root/pkgs/servers/klipper/default.nix
blob: f7dc705d40bdcb8f700715262e54ee78952a834b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
{
  stdenv,
  lib,
  fetchFromGitHub,
  python3,
  python3Packages,
  extraPythonPackages ? ps: [ ],
  unstableGitUpdater,
  makeWrapper,
  writeShellScript,
}:
let
  isCross = (stdenv.hostPlatform != stdenv.buildPlatform);
in
stdenv.mkDerivation rec {
  pname = "klipper";
  version = "0.13.0-unstable-2026-08-18";

  src = fetchFromGitHub {
    owner = "Klipper3d";
    repo = "klipper";
    rev = "60fc7aa67a8da9abb43a2bad825d4992294ebf3f";
    sha256 = "sha256-qs60qBwlD7A0xneNzeNcKfWc8II5rG2oj+tvuhn/aWw=";
  };

  sourceRoot = "${src.name}/klippy";

  # NB: This is needed for the postBuild step
  nativeBuildInputs = [
    python3Packages.cffi
    makeWrapper
  ];

  buildInputs = [
    (python3.withPackages (
      p:
      with p;
      [
        python-can
        cffi
        pyserial
        greenlet
        jinja2
        markupsafe
        numpy
      ]
      ++ extraPythonPackages p
    ))
  ];

  # we need to run this to prebuild the chelper .so. However when cross
  # compiling, a patch is temporarily required during the build process to
  # prevent the build process from using dlopen() on this .so which has been
  # built for a foreign architecture. We then place the unpatched __init__.py
  # back, as this dlopen() call is required at runtime
  postBuild =
    if isCross then
      ''
        python ./chelper/__init__.py
        mv ./chelper/__init__unpatched.py ./chelper/__init__.py
      ''
    else
      ''
        python ./chelper/__init__.py
      '';

  prePatch = lib.optionalString isCross ''
    cp ./chelper/__init__.py ./chelper/__init__unpatched.py
  '';

  patches = lib.optionals isCross [
    # https://github.com/Klipper3d/klipper/pull/7254
    ./cross-ffi.patch
  ];

  # Python 3 is already supported but shebangs aren't updated yet
  postPatch = ''
    for file in klippy.py console.py parsedump.py; do
      substituteInPlace $file \
        --replace-warn '/usr/bin/env python2' '/usr/bin/env python'
    done

    # needed for cross compilation
    substituteInPlace ./chelper/__init__*.py \
      --replace-warn 'GCC_CMD = "gcc"' 'GCC_CMD = "${stdenv.cc.targetPrefix}cc"'
  '';

  pythonInterpreter =
    (python3.withPackages (
      p: with p; [
        numpy
        matplotlib
        python-can
      ]
    )).interpreter;

  pythonScriptWrapper = writeShellScript pname ''
    ${pythonInterpreter} "@out@/lib/scripts/@script@" "$@"
  '';

  # NB: We don't move the main entry point into `/bin`, or even symlink it,
  # because it uses relative paths to find necessary modules. We could wrap but
  # this is used 99% of the time as a service, so it's not worth the effort.
  installPhase = ''
    runHook preInstall
    mkdir -p $out/lib/klipper
    cp -r ./* $out/lib/klipper

    # Moonraker expects `config_examples` and `docs` to be available
    # under `klipper_path`
    cp -r $src/docs $out/lib/docs
    cp -r $src/config $out/lib/config
    cp -r $src/klippy $out/lib/klippy
    mkdir -p $out/lib/scripts
    cp -r $src/scripts/* $out/lib/scripts
    cp $src/lib/katapult/flashtool.py $out/lib/scripts/flash_can.py

    # Add version information. For the normal procedure see https://www.klipper3d.org/Packaging.html#versioning
    # This is done like this because scripts/make_version.py is not available when sourceRoot is set to "${src.name}/klippy"
    echo "${version}-NixOS" > $out/lib/klipper/.version

    mkdir -p $out/bin
    chmod 755 $out/lib/klipper/klippy.py
    makeWrapper $out/lib/klipper/klippy.py $out/bin/klippy --chdir $out/lib/klipper

    substitute "$pythonScriptWrapper" "$out/bin/klipper-calibrate-shaper" \
      --subst-var "out" \
      --subst-var-by "script" "calibrate_shaper.py"
    chmod 755 "$out/bin/klipper-calibrate-shaper"

    substitute "$pythonScriptWrapper" "$out/bin/klipper-canbus-query" \
      --subst-var "out" \
      --subst-var-by "script" "canbus_query.py"
    chmod 755 "$out/bin/klipper-canbus-query"

    runHook postInstall
  '';

  passthru.updateScript = unstableGitUpdater {
    url = meta.homepage;
    tagPrefix = "v";
  };

  meta = {
    description = "Klipper 3D printer firmware";
    mainProgram = "klippy";
    homepage = "https://github.com/Klipper3d/klipper";
    maintainers = with lib.maintainers; [
      lovesegfault
      zhaofengli
      cab404
    ];
    platforms = lib.platforms.linux;
    license = lib.licenses.gpl3Only;
  };
}