From 3796f507edfaa64e62436f4f4190af3ef9c251cb Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Sat, 6 Jan 2024 12:11:05 -0500 Subject: [PATCH] Add out-of-tree qmk post --- content/blog/2024-01-06-out-of-tree-qmk.md | 59 ++++++++++++++++++ .../posts/out-of-tree-qmk/file-tree.png | Bin 0 -> 23429 bytes 2 files changed, 59 insertions(+) create mode 100644 content/blog/2024-01-06-out-of-tree-qmk.md create mode 100644 static/images/posts/out-of-tree-qmk/file-tree.png diff --git a/content/blog/2024-01-06-out-of-tree-qmk.md b/content/blog/2024-01-06-out-of-tree-qmk.md new file mode 100644 index 0000000..08d78d5 --- /dev/null +++ b/content/blog/2024-01-06-out-of-tree-qmk.md @@ -0,0 +1,59 @@ +--- +title: Building QMK Keyboard Firmware Out of Tree +description: Side-stepping the QMK monorepo for my own sanity +date: 2024-01-06 +tags: + - keyboards +draft: true +extra: + auto_center_images: true +aliases: + - /blog/out-of-tree-qmk +--- + +Like many builders and users of custom mechanical keyboards, I make heavy use of the [QMK](https://qmk.fm) keyboard firmware project. It provides functions for doing everything you'd expect a keyboard to be able to do, and is the de-facto standard for [AVR](https://en.wikipedia.org/wiki/AVR_microcontrollers)-based custom keyboards. + +For anyone unfamilliar with QMK, it is a *huge* monorepo containing: + +- The QMK firmware code itself +- A handful of vendored dependencies +- A custom build system +- A tree of per-user firmware configurations for various keyboards + +This means that cloning the QMK Git repository will also pull in everyone's custom keyboard configurations (even though there is very little chance you'll ever use them). + +While I am generally a fan on working in monorepos, I'm not a huge fan of including user-specific code in the main source tree (for this project at least). Additionally, the work required to keep up with the intended flow of the QMK project is a bit much for me considering that I have more important things to spend my time on (like, actually using my keyboard). + +## Working Out of Tree + +To completely side-step the fork-edit-PR cycle of the main QMK project, I've been experimenting with working out of tree. + +This means that my personal firmware source files live in an external repository, and I have a script that: + +1. Fetches a fresh copy of the QMK source code +2. Loads the dependencies +3. Copies my modifications on-top of the QMK source tree +4. Builds the firmware +5. Handles flashing the firmware to my keyboards (one of them has some weird extra steps) + +### My process + +My keyboard source code is organized by board: + +![File tree](/images/posts/out-of-tree-qmk/file-tree.png) + +The contents of these files are exactly like a normal QMK keymap. + +When it comes time to build the firmware, my script will directly copy each keymap into the QMK source tree, correctly modifying file paths to match that of the main repository. + +```text +keyboards/qmk/keymaps/ -> keyboards//keymaps/ewpratten +``` + +Then, it simply passes the build arguments to the QMK buildsystem. + +## Closing thoughts + +This post turned into more of a show-and-tell than my usual walkthrough format. + +Have questions, comments, or want to learn more about my build script? Please [send me an email](/contact). \ No newline at end of file diff --git a/static/images/posts/out-of-tree-qmk/file-tree.png b/static/images/posts/out-of-tree-qmk/file-tree.png new file mode 100644 index 0000000000000000000000000000000000000000..fd954b6ba0b0fd5ba0b897572a239a60914ef68b GIT binary patch literal 23429 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 bPA~43TxZfE`tn0jD> literal 0 HcmV?d00001