~andrew/qmk

ref: 872b1dfb47b16693637cab95a6d3c5e0c2db43ce qmk/flake.nix -rw-r--r-- 7.5 KiB
872b1dfbAndrew Reisner add nix flake. 5 months ago
                                                                                
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
{
  description = "QMK Userspace - Personal keyboard firmware configurations";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    flake-utils.url = "github:numtide/flake-utils";

    # Upstream QMK firmware (for Ploopyco and other standard keyboards)
    qmk-firmware = {
      url = "git+https://github.com/qmk/qmk_firmware?submodules=1";
      flake = false;
    };

    # ZSA fork of QMK firmware (for Voyager, Moonlander, Planck EZ, Ergodox EZ)
    zsa-qmk-firmware = {
      url = "git+https://github.com/zsa/qmk_firmware?submodules=1";
      flake = false;
    };

    # Holykeebs fork of QMK firmware (for Keyball keyboards)
    keyball-qmk-firmware = {
      url = "git+https://github.com/holykeebs/qmk_firmware?submodules=1";
      flake = false;
    };
  };

  outputs = { self, nixpkgs, flake-utils, qmk-firmware, zsa-qmk-firmware, keyball-qmk-firmware }:
    flake-utils.lib.eachDefaultSystem (system:
      let
        pkgs = import nixpkgs {
          inherit system;
          config.allowUnfree = true;
        };

        # Common QMK build dependencies
        qmkBuildInputs = with pkgs; [
          # AVR toolchain (for ATmega-based keyboards like Ploopyco)
          pkgsCross.avr.buildPackages.gcc
          avrdude

          # ARM toolchain (for ARM-based keyboards like Voyager, Keyball)
          gcc-arm-embedded

          # Build tools
          gnumake
          git
          which
          diffutils
          util-linux

          # Python with QMK dependencies
          (python3.withPackages (ps: with ps; [
            pip
            setuptools
            wheel
            pillow
            qmk
            hid
            pyusb
            hjson
            jsonschema
            milc
            pygments
            dotty-dict
          ]))

          # Additional dependencies
          libusb1
          dfu-programmer
          dfu-util
          teensy-loader-cli
          qmk
        ];

        # Helper to link userspace into QMK firmware
        setupUserspace = qmkSrc: pkgs.runCommand "qmk-with-userspace" {} ''
          cp -r ${qmkSrc} $out
          chmod -R +w $out

          # Link keyboards from userspace
          if [ -d ${self}/keyboards ]; then
            for kb in ${self}/keyboards/*; do
              kbname=$(basename $kb)
              if [ -d "$out/keyboards/$kbname" ]; then
                # Merge with existing keyboard (copy keymaps)
                cp -r $kb/* $out/keyboards/$kbname/ 2>/dev/null || true
              else
                # New keyboard, copy entirely
                cp -r $kb $out/keyboards/
              fi
            done
          fi

          # Link users directory
          if [ -d ${self}/users ]; then
            cp -r ${self}/users/* $out/users/ 2>/dev/null || true
          fi

          # Link layouts
          if [ -d ${self}/layouts ]; then
            for layout in ${self}/layouts/*; do
              layoutname=$(basename $layout)
              mkdir -p $out/layouts/$layoutname
              cp -r $layout/* $out/layouts/$layoutname/ 2>/dev/null || true
            done
          fi
        '';

        # QMK sources with userspace integrated
        qmkUpstream = setupUserspace qmk-firmware;
        qmkZsa = setupUserspace zsa-qmk-firmware;
        qmkKeyball = setupUserspace keyball-qmk-firmware;

        # Build a QMK firmware
        buildFirmware = { name, keyboard, keymap, qmkSource }:
          pkgs.stdenv.mkDerivation {
            pname = "qmk-${name}";
            version = "0.1.0";

            src = qmkSource;

            nativeBuildInputs = qmkBuildInputs;

            buildPhase = ''
              runHook preBuild

              # QMK needs a writable home directory
              export HOME="$NIX_BUILD_TOP/home"
              mkdir -p $HOME

              # Build the firmware
              make ${keyboard}:${keymap}

              runHook postBuild
            '';

            installPhase = ''
              runHook preInstall

              mkdir -p $out
              # Copy all generated firmware files
              find . -maxdepth 1 \( -name "*.hex" -o -name "*.bin" -o -name "*.uf2" \) \
                -exec cp {} $out/ \;

              runHook postInstall
            '';

            # Don't try to strip firmware files
            dontStrip = true;
            dontPatchELF = true;
            dontFixup = true;
          };

        # Define keyboard builds
        keyboards = {
          # ZSA Voyager - uses ZSA QMK fork
          voyager-pers = buildFirmware {
            name = "voyager-pers";
            keyboard = "zsa/voyager";
            keymap = "pers";
            qmkSource = qmkZsa;
          };

          voyager-ball = buildFirmware {
            name = "voyager-ball";
            keyboard = "zsa/voyager";
            keymap = "ball";
            qmkSource = qmkZsa;
          };

          voyager-mod = buildFirmware {
            name = "voyager-mod";
            keyboard = "zsa/voyager";
            keymap = "mod";
            qmkSource = qmkZsa;
          };

          # Keyball39 - uses holykeebs QMK fork
          keyball39-init = buildFirmware {
            name = "keyball39-init";
            keyboard = "keyball/keyball39";
            keymap = "init";
            qmkSource = qmkKeyball;
          };

          # Ploopyco Trackball - uses upstream QMK
          ploopyco-trackball-rev1_005-pers = buildFirmware {
            name = "ploopyco-trackball-rev1_005-pers";
            keyboard = "ploopyco/trackball/rev1_005";
            keymap = "pers";
            qmkSource = qmkUpstream;
          };

          ploopyco-trackball-rev1_007-pers = buildFirmware {
            name = "ploopyco-trackball-rev1_007-pers";
            keyboard = "ploopyco/trackball/rev1_007";
            keymap = "pers";
            qmkSource = qmkUpstream;
          };
        };

      in {
        # Individual keyboard packages
        packages = keyboards // {
          # Build all keyboards
          all = pkgs.symlinkJoin {
            name = "qmk-all-keyboards";
            paths = builtins.attrValues keyboards;
          };

          default = self.packages.${system}.all;
        };

        # Development shell with QMK tooling
        devShells.default = pkgs.mkShell {
          buildInputs = qmkBuildInputs ++ (with pkgs; [
            # Additional dev tools
            qmk
          ]);

          shellHook = ''
            echo "QMK Userspace Development Environment"
            echo ""
            echo "Available keyboards:"
            echo "  ZSA (use zsa/qmk_firmware):"
            echo "    - zsa/voyager:pers"
            echo "    - zsa/voyager:ball"
            echo "    - zsa/voyager:mod"
            echo ""
            echo "  Keyball (use holykeebs/qmk_firmware):"
            echo "    - keyball/keyball39:init"
            echo ""
            echo "  Ploopyco (use upstream qmk_firmware):"
            echo "    - ploopyco/trackball/rev1_005:pers"
            echo "    - ploopyco/trackball/rev1_007:pers"
            echo ""
            echo "Build with: nix build .#<keyboard-name>"
            echo "  e.g.: nix build .#voyager-pers"
            echo "        nix build .#keyball39-init"
            echo "        nix build .#all"
            echo ""
          '';
        };

        # Apps for flashing firmware
        apps = {
          flash-voyager = flake-utils.lib.mkApp {
            drv = pkgs.writeShellScriptBin "flash-voyager" ''
              ${pkgs.wally-cli}/bin/wally-cli ${self.packages.${system}.voyager-pers}/*.bin
            '';
          };
        };
      }
    );
}