build(nix): one root flake for Windows, KDE Plasma and macOS

One flake.nix and one flake.lock replace the root flake that re-exported two nested flakes.
The inputs are one nixpkgs (the revision the KDE flake was locked to, f05535a0: the VM suites
run against its Plasma and KWin 6.8.2), one crane, rust-overlay (19ecdd08, rustc 1.99.1),
flake-parts, treefmt-nix and git-hooks-nix, all the KDE flake revisions, plus asc. A second
nixpkgs, nixpkgs-x86_64-darwin (16c7794d, the macOS flake pin), serves the x86_64-darwin
outputs only: evaluating that system with the unified pin throws (nixpkgs 26.11 dropped it).

Layout: nix/common.nix (systems, pkgs, treefmt, pre-commit, dev shells default and native,
default overlay), nix/lib.nix, nix/workspace-src.nix (one source per platform from the one Cargo
workspace: the platform members, its files of the shared crates told apart by name, the data
files, and stubs for the other members, which cargo needs), nix/{windows,linux,macos}/
{build,flake-module}.nix. The dependency derivations use a manifests-only dummy source, a plain
path, so crane reads nothing built at evaluation and the Darwin outputs evaluate on Linux.

Names: unprefixed outputs are the native build (Linux: KDE; Darwin: macOS); Windows cross
outputs are windows-*; checks are windows-*, linux-*, macos-* and vm-NAME; table in docs/dev/flake.md
(next revision). One deny.toml, one format-all and one version (workspace) are used by all.
PLACEHOLDER for the agent that owns the check sets: the checks that evaluate a NixOS system
or a VM (vm-*, linux-nixos-module-eval, linux-plasma-guests-eval, linux-run-kde*) are
legacyPackages.<system>.heavyChecks.<name>, not checks, because evaluating them needs more
memory than nix flake check --all-systems can use. The Plasma guests got a stub root file system
and boot loader (mkDefault, replaced by their vmVariant) so that nix flake check can evaluate
their toplevel.

Verified: nix flake check --no-build --all-systems passes (42 s, rc 0); nix flake check
--no-build passes. Not verified: any build (next revisions record them).
This commit is contained in:
LGUG2Z committed 2026-11-05 15:03:27 -08:00
1 parent 05698a34ba
commit ae9ee327f9
11 files changed
+1821 -1084

No files matched your search

Generated
+14 -364
View File
@@ -17,98 +17,6 @@
}
},
"crane": {
"locked": {
"lastModified": 1766774972,
"narHash": "sha256-8qxEFpj4dVmIuPn9j9z6NTbU+hrcGjBOvaxTzre5HmM=",
"owner": "ipetkov",
"repo": "crane",
"rev": "01bc1d404a51a0a07e9d8759cd50a7903e218c82",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1761588595,
"narHash": "sha256-XKUZz9zewJNUj46b4AJdiRZJAvSZ0Dqj2BNfXvFlJC4=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1765835352,
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"git-hooks-nix": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1765911976,
"narHash": "sha256-t3T/xm8zstHRLx+pIHxVpQTiySbKqcQbK+r+01XVKc0=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "b68b780b69702a090c8bb1b973bab13756cc7a27",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"git-hooks-nix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"kfde-crane": {
"locked": {
"lastModified": 1792350552,
"narHash": "sha256-292lvpvxTGKryKxh3/3tcIVi85CwYGdl4oOi113Sob8=",
@@ -120,11 +28,10 @@
"original": {
"owner": "ipetkov",
"repo": "crane",
"rev": "e675a36b7377bebe41d548388a2501cbb16d380d",
"type": "github"
}
},
"kfde-flake-compat": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
@@ -137,15 +44,12 @@
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
}
},
"kfde-flake-parts": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"kfde-nixpkgs-lib"
]
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1790898470,
@@ -158,17 +62,14 @@
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "024633cd702b10285db5cb19b40ad48d2399ba60",
"type": "github"
}
},
"kfde-git-hooks-nix": {
"git-hooks-nix": {
"inputs": {
"flake-compat": [
"kfde-flake-compat"
],
"flake-compat": "flake-compat",
"nixpkgs": [
"kfde-nixpkgs"
"nixpkgs"
]
},
"locked": {
@@ -182,11 +83,10 @@
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "1ba1dbfe28738c9a71c266be6efa5b89e75491a9",
"type": "github"
}
},
"kfde-nixpkgs": {
"nixpkgs": {
"locked": {
"lastModified": 1793136825,
"narHash": "sha256-2pkE69JpFRInaARSczZytcfPw83IL82E2KdhbWGwdMs=",
@@ -197,12 +97,12 @@
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"rev": "f05535a0efcb2134c478d1e88434fadf486d42b5",
"type": "github"
}
},
"kfde-nixpkgs-lib": {
"nixpkgs-lib": {
"locked": {
"lastModified": 1790477367,
"narHash": "sha256-oujbxmVKDhci2E4EM3KyAbRUgp5/IsCoiA/LfEF7OOk=",
@@ -214,155 +114,10 @@
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "f7cd230690d9fc982d06129f9189b101fddcfa3b",
"type": "github"
}
},
"kfde-rust-overlay": {
"inputs": {
"nixpkgs": [
"kfde-nixpkgs"
]
},
"locked": {
"lastModified": 1793174968,
"narHash": "sha256-u5nUf4cewaT36iIzG1s/VECktqUMiZaGF5BsgervsvQ=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "19ecdd08213836b36c4296a3a4f4af5a3d5308db",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "19ecdd08213836b36c4296a3a4f4af5a3d5308db",
"type": "github"
}
},
"kfde-treefmt-nix": {
"inputs": {
"nixpkgs": [
"kfde-nixpkgs"
]
},
"locked": {
"lastModified": 1791885023,
"narHash": "sha256-C3alQ2MEt0zVrUSho5NUDo3jmhbff72++bfwoqT9Omo=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "0d6d739a833ba9a9c93034862e4a7b061a062a1c",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "0d6d739a833ba9a9c93034862e4a7b061a062a1c",
"type": "github"
}
},
"kfm-crane": {
"locked": {
"lastModified": 1767461147,
"narHash": "sha256-TH/xTeq/RI+DOzo+c+4F431eVuBpYVwQwBxzURe7kcI=",
"owner": "ipetkov",
"repo": "crane",
"rev": "7d59256814085fd9666a2ae3e774dc5ee216b630",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"rev": "7d59256814085fd9666a2ae3e774dc5ee216b630",
"type": "github"
}
},
"kfm-flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
}
},
"kfm-flake-parts": {
"inputs": {
"nixpkgs-lib": [
"kfm-nixpkgs-lib"
]
},
"locked": {
"lastModified": 1765835352,
"narHash": "sha256-XswHlK/Qtjasvhd1nOa1e8MgZ8GS//jBoTqWtrS1Giw=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "a34fae9c08a15ad73f295041fec82323541400a9",
"type": "github"
}
},
"kfm-git-hooks-nix": {
"inputs": {
"flake-compat": [
"kfm-flake-compat"
],
"gitignore": [
"kfm-gitignore"
],
"nixpkgs": [
"kfm-nixpkgs"
]
},
"locked": {
"lastModified": 1767281941,
"narHash": "sha256-6MkqajPICgugsuZ92OMoQcgSHnD6sJHwk8AxvMcIgTE=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "f0927703b7b1c8d97511c4116eb9b4ec6645a0fa",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "f0927703b7b1c8d97511c4116eb9b4ec6645a0fa",
"type": "github"
}
},
"kfm-gitignore": {
"inputs": {
"nixpkgs": [
"kfm-nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
}
},
"kfm-nixpkgs": {
"nixpkgs-x86_64-darwin": {
"locked": {
"lastModified": 1767364772,
"narHash": "sha256-fFUnEYMla8b7UKjijLnMe+oVFOz6HjijGGNS1l7dYaQ=",
@@ -378,119 +133,14 @@
"type": "github"
}
},
"kfm-nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
}
},
"kfm-rust-overlay": {
"inputs": {
"nixpkgs": [
"kfm-nixpkgs"
]
},
"locked": {
"lastModified": 1793174968,
"narHash": "sha256-u5nUf4cewaT36iIzG1s/VECktqUMiZaGF5BsgervsvQ=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "19ecdd08213836b36c4296a3a4f4af5a3d5308db",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "19ecdd08213836b36c4296a3a4f4af5a3d5308db",
"type": "github"
}
},
"kfm-treefmt-nix": {
"inputs": {
"nixpkgs": [
"kfm-nixpkgs"
]
},
"locked": {
"lastModified": 1767468822,
"narHash": "sha256-MpffQxHxmjVKMiQd0Tg2IM/bSjjdQAM+NDcX6yxj7rE=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "d56486eb9493ad9c4777c65932618e9c2d0468fc",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "d56486eb9493ad9c4777c65932618e9c2d0468fc",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1766870016,
"narHash": "sha256-fHmxAesa6XNqnIkcS6+nIHuEmgd/iZSP/VXxweiEuQw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5c2bc52fb9f8c264ed6c93bd20afa2ff5e763dce",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1765674936,
"narHash": "sha256-k00uTP4JNfmejrCLJOwdObYC9jHRrr/5M/a/8L2EIdo=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "2075416fcb47225d9b68ac469a5c4801a9c4dd85",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"asc": "asc",
"crane": "crane",
"flake-parts": "flake-parts",
"git-hooks-nix": "git-hooks-nix",
"kfde-crane": "kfde-crane",
"kfde-flake-compat": "kfde-flake-compat",
"kfde-flake-parts": "kfde-flake-parts",
"kfde-git-hooks-nix": "kfde-git-hooks-nix",
"kfde-nixpkgs": "kfde-nixpkgs",
"kfde-nixpkgs-lib": "kfde-nixpkgs-lib",
"kfde-rust-overlay": "kfde-rust-overlay",
"kfde-treefmt-nix": "kfde-treefmt-nix",
"kfm-crane": "kfm-crane",
"kfm-flake-compat": "kfm-flake-compat",
"kfm-flake-parts": "kfm-flake-parts",
"kfm-git-hooks-nix": "kfm-git-hooks-nix",
"kfm-gitignore": "kfm-gitignore",
"kfm-nixpkgs": "kfm-nixpkgs",
"kfm-nixpkgs-lib": "kfm-nixpkgs-lib",
"kfm-rust-overlay": "kfm-rust-overlay",
"kfm-treefmt-nix": "kfm-treefmt-nix",
"nixpkgs": "nixpkgs",
"nixpkgs-x86_64-darwin": "nixpkgs-x86_64-darwin",
"rust-overlay": "rust-overlay",
"treefmt-nix": "treefmt-nix"
}
@@ -522,11 +172,11 @@
]
},
"locked": {
"lastModified": 1766000401,
"narHash": "sha256-+cqN4PJz9y0JQXfAK5J1drd0U05D5fcAGhzhfVrDlsI=",
"lastModified": 1791885023,
"narHash": "sha256-C3alQ2MEt0zVrUSho5NUDo3jmhbff72++bfwoqT9Omo=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "42d96e75aa56a3f70cab7e7dc4a32868db28e8fd",
"rev": "0d6d739a833ba9a9c93034862e4a7b061a062a1c",
"type": "github"
},
"original": {
+14 -704
View File
@@ -1,8 +1,15 @@
{
description = "komorebi for Windows";
description = "komorebi, a tiling window manager for Windows, KDE Plasma and macOS";
inputs = {
# One nixpkgs for the three platforms. The KDE VM tests run against the Plasma and KWin of this
# revision (nix/linux/supported-kwin.nix says which KWin that is), so it is the one an update has
# to keep green for the VM suites; see docs/dev/flake.md.
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
# Intel Macs only: nixpkgs 26.11 dropped x86_64-darwin (evaluating it throws), so the x86_64-darwin
# outputs use the last revision that still supported it, the one the macOS flake was pinned to.
# Nothing else uses it; drop it with x86_64-darwin when Intel Macs are no longer supported.
nixpkgs-x86_64-darwin.url = "github:NixOS/nixpkgs/16c7794d0a28b5a37904d55bcca36003b9109aaa";
flake-parts.url = "github:hercules-ci/flake-parts";
crane.url = "github:ipetkov/crane";
rust-overlay.url = "github:oxalica/rust-overlay";
@@ -11,718 +18,21 @@
treefmt-nix.inputs.nixpkgs.follows = "nixpkgs";
git-hooks-nix.url = "github:cachix/git-hooks.nix";
git-hooks-nix.inputs.nixpkgs.follows = "nixpkgs";
# the application-specific configuration that komorebic embeds on Windows
asc = {
url = "github:LGUG2Z/komorebi-application-specific-configuration";
flake = false;
};
# the other platforms' flakes (komorebi-for-kde/flake.nix, komorebi-for-mac/flake.nix) are not inputs: their
# flake.nix files are imported below and called with these inputs, which are pinned to exactly the
# revisions of their flake.lock (so nothing here pins the content of those directories)
# inputs of komorebi-for-kde, pinned to the revisions in komorebi-for-kde/flake.lock (keep them equal to it)
kfde-nixpkgs.url = "github:NixOS/nixpkgs/f05535a0efcb2134c478d1e88434fadf486d42b5";
kfde-nixpkgs-lib.url = "github:nix-community/nixpkgs.lib/f7cd230690d9fc982d06129f9189b101fddcfa3b";
kfde-flake-compat = { url = "github:NixOS/flake-compat/5edf11c44bc78a0d334f6334cdaf7d60d732daab"; flake = false; };
kfde-flake-parts.url = "github:hercules-ci/flake-parts/024633cd702b10285db5cb19b40ad48d2399ba60";
kfde-flake-parts.inputs.nixpkgs-lib.follows = "kfde-nixpkgs-lib";
kfde-crane.url = "github:ipetkov/crane/e675a36b7377bebe41d548388a2501cbb16d380d";
kfde-rust-overlay.url = "github:oxalica/rust-overlay/19ecdd08213836b36c4296a3a4f4af5a3d5308db";
kfde-rust-overlay.inputs.nixpkgs.follows = "kfde-nixpkgs";
kfde-treefmt-nix.url = "github:numtide/treefmt-nix/0d6d739a833ba9a9c93034862e4a7b061a062a1c";
kfde-treefmt-nix.inputs.nixpkgs.follows = "kfde-nixpkgs";
kfde-git-hooks-nix.url = "github:cachix/git-hooks.nix/1ba1dbfe28738c9a71c266be6efa5b89e75491a9";
kfde-git-hooks-nix.inputs.nixpkgs.follows = "kfde-nixpkgs";
kfde-git-hooks-nix.inputs.flake-compat.follows = "kfde-flake-compat";
# inputs of komorebi-for-mac, pinned to the revisions in komorebi-for-mac/flake.lock (keep them equal to it)
kfm-nixpkgs.url = "github:NixOS/nixpkgs/16c7794d0a28b5a37904d55bcca36003b9109aaa";
kfm-nixpkgs-lib.url = "github:nix-community/nixpkgs.lib/2075416fcb47225d9b68ac469a5c4801a9c4dd85";
kfm-flake-compat = { url = "github:NixOS/flake-compat/5edf11c44bc78a0d334f6334cdaf7d60d732daab"; flake = false; };
kfm-flake-parts.url = "github:hercules-ci/flake-parts/a34fae9c08a15ad73f295041fec82323541400a9";
kfm-flake-parts.inputs.nixpkgs-lib.follows = "kfm-nixpkgs-lib";
kfm-crane.url = "github:ipetkov/crane/7d59256814085fd9666a2ae3e774dc5ee216b630";
kfm-rust-overlay.url = "github:oxalica/rust-overlay/19ecdd08213836b36c4296a3a4f4af5a3d5308db";
kfm-rust-overlay.inputs.nixpkgs.follows = "kfm-nixpkgs";
kfm-treefmt-nix.url = "github:numtide/treefmt-nix/d56486eb9493ad9c4777c65932618e9c2d0468fc";
kfm-treefmt-nix.inputs.nixpkgs.follows = "kfm-nixpkgs";
kfm-git-hooks-nix.url = "github:cachix/git-hooks.nix/f0927703b7b1c8d97511c4116eb9b4ec6645a0fa";
kfm-git-hooks-nix.inputs.nixpkgs.follows = "kfm-nixpkgs";
kfm-git-hooks-nix.inputs.flake-compat.follows = "kfm-flake-compat";
kfm-gitignore = { url = "github:hercules-ci/gitignore.nix/637db329424fd7e46cf4185293b9cc8c88c95394"; inputs.nixpkgs.follows = "kfm-nixpkgs"; };
kfm-git-hooks-nix.inputs.gitignore.follows = "kfm-gitignore";
};
outputs =
inputs@{
self,
nixpkgs,
flake-parts,
crane,
rust-overlay,
...
}:
let
windowsSdkVersion = "10.0.26100";
windowsCrtVersion = "14.44.17.14";
mkWindowsSdk =
pkgs:
pkgs.stdenvNoCC.mkDerivation {
name = "windows-sdk-${windowsSdkVersion}-crt-${windowsCrtVersion}";
nativeBuildInputs = [
pkgs.xwin
pkgs.cacert
];
outputHashAlgo = "sha256";
outputHashMode = "recursive";
# hash of `xwin splat --disable-symlinks`: without the flag xwin makes
# case-alias symlinks on a case-sensitive filesystem and none on a
# case-insensitive one, so the hash depends on the host
outputHash = "sha256-YjmEfoxtcmQXvUc+WtaElnyP/4osNvtSI7X6MDaNTjg=";
buildCommand = ''
export HOME=$(mktemp -d)
# keep the cache on the same filesystem as $out, as xwin moves
# files from the cache into the output and rename(2) fails with
# EXDEV when the build dir and the store are separate mounts
mkdir -p $out
xwin --accept-license \
--cache-dir $out/.xwin-cache \
--sdk-version ${windowsSdkVersion} \
--crt-version ${windowsCrtVersion} \
splat --disable-symlinks --output $out
rm -rf $out/.xwin-cache
'';
};
# the linker asks for `kernel32.lib` of a file that is `Kernel32.lib`, so
# on a case-sensitive host add a lowercase alias for every name with a
# capital in it, and the spellings the headers include each other by (the
# SDK itself is made without symlinks)
caseFolded =
{ pkgs, windowsSdk }:
if pkgs.stdenv.hostPlatform.isDarwin then
windowsSdk
else
pkgs.runCommandLocal "${windowsSdk.name}-case-folded" { } ''
cp -rs ${windowsSdk} $out
chmod -R u+w $out
cd $out
find . -name '*[A-Z]*' | while read -r path; do
directory=$(dirname "$path")
name=$(basename "$path")
lower=$(echo "$name" | tr 'A-Z' 'a-z')
if [ ! -e "$directory/$lower" ] && [ ! -L "$directory/$lower" ]; then
ln -s "$name" "$directory/$lower"
fi
done
# headers include each other with spellings that differ from the
# file name (`DriverSpecs.h` for `driverspecs.h`), which xwin
# covers with symlinks unless told not to
grep -Rhoi -E '^[[:space:]]*#[[:space:]]*include[[:space:]]*[<"][^>"]+[>"]' \
crt/include sdk/include 2>/dev/null \
| sed -E 's/.*[<"]([^>"]+)[>"]/\1/; s#\\#/#g; s#.*/##' \
| sort -u > $TMPDIR/spellings
find crt/include sdk/include \( -type f -o -type l \) \
| awk -v spellings=$TMPDIR/spellings '
BEGIN {
while ((getline line < spellings) > 0)
known[tolower(line)] = known[tolower(line)] "\n" line
}
{
n = split($0, parts, "/"); base = parts[n]
dir = substr($0, 1, length($0) - length(base))
m = split(known[tolower(base)], names, "\n")
for (i = 1; i <= m; i++)
if (names[i] != "" && names[i] != base)
print dir "\t" base "\t" names[i]
}' \
| while IFS=$'\t' read -r directory name spelling; do
if [ ! -e "$directory$spelling" ] && [ ! -L "$directory$spelling" ]; then
ln -s "$name" "$directory$spelling"
fi
done
'';
mkMsvcEnv =
{ pkgs, windowsSdk }:
let
sdk = caseFolded { inherit pkgs windowsSdk; };
clangVersion = pkgs.lib.versions.major pkgs.llvmPackages.clang.version;
in
{
# linker for the windows target
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER = "lld-link";
# c/c++ compiler
CC_x86_64_pc_windows_msvc = "clang-cl";
CXX_x86_64_pc_windows_msvc = "clang-cl";
AR_x86_64_pc_windows_msvc = "llvm-lib";
# IMPORTANT: libclang include path MUST come first to avoid header conflicts
CFLAGS_x86_64_pc_windows_msvc = builtins.concatStringsSep " " [
"--target=x86_64-pc-windows-msvc"
"-Wno-unused-command-line-argument"
"-fuse-ld=lld-link"
"/imsvc${pkgs.llvmPackages.libclang.lib}/lib/clang/${clangVersion}/include"
"/imsvc${sdk}/crt/include"
"/imsvc${sdk}/sdk/include/ucrt"
"/imsvc${sdk}/sdk/include/um"
"/imsvc${sdk}/sdk/include/shared"
];
CXXFLAGS_x86_64_pc_windows_msvc = builtins.concatStringsSep " " [
"--target=x86_64-pc-windows-msvc"
"-Wno-unused-command-line-argument"
"-fuse-ld=lld-link"
"/imsvc${pkgs.llvmPackages.libclang.lib}/lib/clang/${clangVersion}/include"
"/imsvc${sdk}/crt/include"
"/imsvc${sdk}/sdk/include/ucrt"
"/imsvc${sdk}/sdk/include/um"
"/imsvc${sdk}/sdk/include/shared"
];
# target-specific rust flags with linker flavor and library search paths
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS = builtins.concatStringsSep " " [
"-Clinker-flavor=lld-link"
"-Lnative=${sdk}/crt/lib/x86_64"
"-Lnative=${sdk}/sdk/lib/um/x86_64"
"-Lnative=${sdk}/sdk/lib/ucrt/x86_64"
];
# cargo target
CARGO_BUILD_TARGET = "x86_64-pc-windows-msvc";
};
mkKomorebiPackages =
{
pkgs,
windowsSdk,
# the commit stamped into the binaries; a build for the e2e guest
# gives a constant one, so that a new commit does not rebuild it
commit ? self.rev or (pkgs.lib.removeSuffix "-dirty" (self.dirtyRev or "unknown")),
}:
let
# toolchain with windows msvc target
toolchain = (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml).override {
targets = [ "x86_64-pc-windows-msvc" ];
};
craneLib = (crane.mkLib pkgs).overrideToolchain toolchain;
version = "0.1.0";
msvcEnv = mkMsvcEnv { inherit pkgs windowsSdk; };
# the files of the Windows crates; the KDE and macOS crates are in the same Cargo workspace and
# are added as stubs (nix/workspace-src.nix), so that an edit of one changes nothing here
real = pkgs.lib.cleanSourceWith {
src = ./.;
filter =
path: type:
# the other platforms' crates are added as stubs below
!(pkgs.lib.hasInfix "/komorebi-for-kde" path || pkgs.lib.hasInfix "/komorebi-for-mac" path)
# the Linux and macOS files of komorebic are not read when building for Windows
&& !(builtins.match ".*/komorebi(c/(src/(linux|macos)|tests/(linux|macos)_)|-client/(src/(linux|macos)\\.rs|tests/(linux|macos)_)|-bar/src/macos|-daemon/tests/(linux|macos)_)[^/]*.*" path != null)
&& (
(craneLib.filterCargoSources path type)
|| (pkgs.lib.hasInfix "/docs/" path)
|| (builtins.match ".*/docs/.*" path != null)
|| (builtins.match ".*/komorebic/fixtures/.*" path != null)
);
};
src = import ./nix/workspace-src.nix {
inherit pkgs real;
repoRoot = ./.;
isReal = member: !(pkgs.lib.hasPrefix "komorebi-for-" member);
};
lockFile = builtins.path {
path = ./Cargo.lock;
name = "Cargo.lock";
};
tomlFile = builtins.path {
path = ./Cargo.toml;
name = "Cargo.toml";
};
commonArgs = {
inherit src version;
# the lock and the root manifest as files of their own: a path inside the filtered source would put the
# whole filtered source into the context of every derivation, and then an edit of any file
# would change the dependencies
cargoLock = lockFile;
cargoToml = tomlFile;
cargoVendorDir = craneLib.vendorCargoDeps { cargoLock = lockFile; };
strictDeps = true;
# applications.json is committed now; this copies the pinned asc input over it, so that the
# embedded file is the pinned one even when the committed copy is older
preBuild = "mkdir -p komorebic && cp ${inputs.asc}/applications.json komorebic/applications.json";
# a constant, so that the dependencies and every check are the same
# derivations for every commit; only fullBuild stamps the real one
COMMIT_HASH = "nix-check";
# build inputs for cross-compilation
nativeBuildInputs = [
pkgs.llvmPackages.clang-unwrapped
pkgs.llvmPackages.lld
pkgs.llvmPackages.llvm
# komorebic build script uses reqwest, which runs on the build host
pkgs.pkg-config
pkgs.openssl
pkgs.openssl.dev
];
# point the host-side openssl-sys build at the nix openssl
OPENSSL_NO_VENDOR = "1";
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include";
# cross-compilation environment
inherit (msvcEnv)
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER
CC_x86_64_pc_windows_msvc
CXX_x86_64_pc_windows_msvc
AR_x86_64_pc_windows_msvc
CFLAGS_x86_64_pc_windows_msvc
CXXFLAGS_x86_64_pc_windows_msvc
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS
CARGO_BUILD_TARGET
;
};
cargoArtifacts = craneLib.buildDepsOnly commonArgs;
individualCrateArgs = commonArgs // {
inherit cargoArtifacts;
doCheck = false;
doDoc = false;
};
fullBuild = craneLib.buildPackage (
individualCrateArgs
// {
pname = "komorebi-workspace";
COMMIT_HASH = commit;
}
);
extractBinary =
binaryName:
pkgs.runCommand "komorebi-${binaryName}"
{
meta = fullBuild.meta // { };
}
''
mkdir -p $out/bin
cp ${fullBuild}/bin/${binaryName}.exe $out/bin/
'';
in
{
inherit
craneLib
src
real
individualCrateArgs
fullBuild
msvcEnv
;
komorebi = extractBinary "komorebi";
komorebic = extractBinary "komorebic";
komorebic-no-console = extractBinary "komorebic-no-console";
komorebi-bar = extractBinary "komorebi-bar";
komorebi-gui = extractBinary "komorebi-gui";
komorebi-shortcuts = extractBinary "komorebi-shortcuts";
komorebi-testwin = extractBinary "komorebi-testwin";
komorebi-e2e-agent = extractBinary "komorebi-e2e-agent";
};
mkPkgs =
system:
import nixpkgs {
inherit system;
overlays = [ (import rust-overlay) ];
};
in
inputs@{ flake-parts, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"aarch64-darwin"
"x86_64-linux"
"aarch64-linux"
];
imports = [
inputs.treefmt-nix.flakeModule
inputs.git-hooks-nix.flakeModule
# komorebi for KDE Plasma (kfde) and komorebi for macOS (kfm), re-exported with a prefix.
# Their flakes keep their own inputs and lock files (inputs.<name>.inputs), but their
# outputs are made here, from the whole repository as the source: the Rust workspaces of
# both depend on the crates of this tree by path, which a flake of their own directory
# cannot see (their `lib.mkOutputs`). The VM tests of kfde are not re-exported as checks,
# so that `nix flake check` of this flake does not boot them; they are reachable as
# legacyPackages.<system>.kfde-vm.
(
{ lib, ... }:
let
# the nested flake.nix files are plain attribute sets: import them and call their
# `lib.mkOutputs` with the inputs declared above (same revisions as their own locks).
# `self` of a nested flake: its own outputs, with the revision and the path of this one
nestedOutputs =
name: dir:
let
nestedInputs = lib.genAttrs [
"nixpkgs"
"flake-parts"
"crane"
"rust-overlay"
"treefmt-nix"
"git-hooks-nix"
] (n: inputs."${name}-${n}");
mkOutputs =
((import (self.outPath + "/${dir}/flake.nix")).outputs (nestedInputs // { inherit self; })).lib.mkOutputs;
nestedSelf =
outputs
// lib.filterAttrs (n: _: n == "rev" || n == "dirtyRev") self
// {
outPath = self.outPath + "/${dir}";
inputs = nestedInputs;
};
outputs = mkOutputs {
self = nestedSelf;
inputs = nestedInputs // {
self = nestedSelf;
};
repoRoot = self;
platformDir = dir;
};
in
outputs;
kfde = nestedOutputs "kfde" "komorebi-for-kde";
kfm = nestedOutputs "kfm" "komorebi-for-mac";
in
{
flake = {
nixosModules.komorebi-for-kde = kfde.nixosModules.default;
overlays.komorebi-for-kde = kfde.overlays.default;
darwinModules.komorebi-for-mac = kfm.darwinModules.default;
};
perSystem =
{ system, ... }:
let
prefixed = prefix: lib.mapAttrs' (name: lib.nameValuePair "${prefix}-${name}");
withoutVmTests = lib.filterAttrs (name: _: !(lib.hasPrefix "vm-" name));
in
{
packages =
prefixed "kfde" (kfde.packages.${system} or { }) // prefixed "kfm" (kfm.packages.${system} or { });
checks =
prefixed "kfde" (withoutVmTests (kfde.checks.${system} or { }))
// prefixed "kfm" (kfm.checks.${system} or { });
legacyPackages.kfde-vm = (kfde.legacyPackages.${system} or { }).vm or { };
};
}
)
./nix/common.nix
./nix/windows/flake-module.nix
./nix/linux/flake-module.nix
./nix/macos/flake-module.nix
];
perSystem =
{ config, system, ... }:
let
pkgs = mkPkgs system;
windowsSdk = mkWindowsSdk pkgs;
build = mkKomorebiPackages { inherit pkgs windowsSdk; };
# the binaries the e2e guest runs: the same build, but with a
# constant commit stamp, so a new commit (or a dirty tree) that does
# not change a source file reuses it. The commit under test is
# KOMOREBI_E2E_COMMIT, not what the binary says.
e2eBuild = mkKomorebiPackages {
inherit pkgs windowsSdk;
commit = "e2e";
};
# OVMF without SMM and with the TPM support Windows 11 Setup asks for
ovmfWithTpm = (pkgs.OVMF.override { tpmSupport = true; }).fd;
e2eWindows = pkgs.writeShellApplication {
name = "e2e-windows";
runtimeInputs = [
pkgs.qemu
pkgs.swtpm
(pkgs.python3.withPackages (p: [ p.pillow ]))
];
text = ''
: "''${E2E_GOLDEN:?set E2E_GOLDEN to the golden image directory}"
out=''${1:?usage: e2e-windows OUT SCENARIO...}
shift
# the binaries under test: this checkout's, or those of another
# commit, made by e2e/platforms/qemu_windows/build-ref.sh
if [ -n "''${KOMOREBI_E2E_BINS:-}" ]; then
bins=$KOMOREBI_E2E_BINS
if [ -f "$bins/commit" ]; then
KOMOREBI_E2E_COMMIT=$(cat "$bins/commit")
fi
else
bins=$(mktemp -d)
trap 'rm -rf "$bins"' EXIT
cp ${e2eBuild.komorebi}/bin/komorebi.exe "$bins"
cp ${e2eBuild.komorebic}/bin/komorebic.exe "$bins"
cp ${e2eBuild.komorebi-testwin}/bin/komorebi-testwin.exe "$bins"
cp ${e2eBuild.komorebi-bar}/bin/komorebi-bar.exe "$bins"
fi
export OVMF_FD=${ovmfWithTpm}
export KOMOREBI_E2E_BAR_SCHEMA=${./schema.bar.json}
export KOMOREBI_E2E_COMMIT=''${KOMOREBI_E2E_COMMIT:-${self.rev or "dirty"}}
export PYTHONPATH=${./e2e/lib}:${./e2e}
exec python -m platforms.qemu_windows.run \
"$E2E_GOLDEN" "$bins" "$out" "$@"
'';
};
# toolchain with windows target and nightly rustfmt
rustToolchain = (pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml).override {
targets = [ "x86_64-pc-windows-msvc" ];
};
nightlyRustfmt = pkgs.rust-bin.nightly.latest.rustfmt;
rustToolchainWithNightlyRustfmt = pkgs.symlinkJoin {
name = "rust-toolchain-with-nightly-rustfmt";
paths = [
nightlyRustfmt
rustToolchain
];
};
nightlyToolchain = pkgs.rust-bin.nightly.latest.default.override {
targets = [ "x86_64-pc-windows-msvc" ];
};
cargo-udeps = pkgs.writeShellScriptBin "cargo-udeps" ''
export PATH="${nightlyToolchain}/bin:$PATH"
exec ${pkgs.cargo-udeps}/bin/cargo-udeps "$@"
'';
in
{
treefmt = {
projectRootFile = "flake.nix";
programs = {
deadnix.enable = true;
just.enable = true;
nixfmt.enable = true;
taplo.enable = true;
rustfmt = {
enable = true;
package = pkgs.rust-bin.nightly.latest.rustfmt;
};
};
};
checks = {
komorebi-workspace-clippy = build.craneLib.cargoClippy (
build.individualCrateArgs
// {
cargoClippyExtraArgs = "--all-targets -- -D warnings";
}
);
# the nightly rustfmt over the Windows files, as tools/format-all --tree windows does locally. Not
# `cargo fmt`: it ran the stable rustfmt (which ignores the nightly options of rustfmt.toml) over
# the stub sources of the other platforms and followed `mod linux;` into files the Windows source
# does not have.
komorebi-workspace-fmt =
pkgs.runCommand "komorebi-workspace-fmt"
{
nativeBuildInputs = [ nightlyRustfmt ];
}
''
RUSTFMT=rustfmt bash ${./tools/format-all} --check --tree windows --root ${build.real}
touch $out
'';
komorebi-workspace-toml-fmt = build.craneLib.taploFmt {
src = pkgs.lib.sources.sourceFilesBySuffices build.real [ ".toml" ];
};
# cargo deny sees every member of the workspace: leave out the KDE and macOS packages, which
# have their own policy (komorebi-for-*/deny.toml) in their own checks
komorebi-workspace-deny = build.craneLib.cargoDeny {
inherit (build) src;
pname = "komorebi-workspace";
version = "0.1.0";
cargoToml = "${build.real}/Cargo.toml";
cargoLock = "${build.real}/Cargo.lock";
cargoDenyExtraArgs =
"--workspace "
+ pkgs.lib.concatMapStringsSep " " (p: "--exclude ${p}") [
"komorebi-linux"
"komorebic-linux"
"komorebi-client-linux"
"komorebi-hotkeys"
"version-linux"
"komorebi-macos"
"komorebic-macos"
"komorebi-client-macos"
"komorebi-bar-macos"
"version-macos"
];
};
komorebi-workspace-nextest = build.craneLib.cargoNextest build.individualCrateArgs;
# the Python of the Windows e2e guest that needs no guest: the
# vendored core's tests and those of the runner
e2e-tests =
pkgs.runCommand "e2e-tests"
{
nativeBuildInputs = [
pkgs.git
(pkgs.python3.withPackages (p: [
p.pillow
p.pytest
]))
];
}
''
cp -r ${./e2e} e2e
chmod -R u+w e2e
cd e2e
export HOME=$TMPDIR PYTHONPATH=$PWD/lib:$PWD
python -m pytest -q -p no:cacheprovider lib/tests platforms/qemu_windows/tests
touch $out
'';
};
packages = {
inherit (build)
komorebi
komorebic
komorebic-no-console
komorebi-bar
komorebi-gui
komorebi-shortcuts
komorebi-testwin
komorebi-e2e-agent
;
inherit windowsSdk;
# the test window of the Windows e2e guest, built with the constant
# stamp: a new commit does not rebuild what it depends on
komorebi-testwin-e2e = e2eBuild.komorebi-testwin;
komorebi-full = build.fullBuild;
default = build.fullBuild;
};
apps = {
komorebi = {
type = "app";
program = "${build.komorebi}/bin/komorebi.exe";
};
komorebic = {
type = "app";
program = "${build.komorebic}/bin/komorebic.exe";
};
komorebic-no-console = {
type = "app";
program = "${build.komorebic-no-console}/bin/komorebic-no-console.exe";
};
komorebi-bar = {
type = "app";
program = "${build.komorebi-bar}/bin/komorebi-bar.exe";
};
komorebi-gui = {
type = "app";
program = "${build.komorebi-gui}/bin/komorebi-gui.exe";
};
komorebi-shortcuts = {
type = "app";
program = "${build.komorebi-shortcuts}/bin/komorebi-shortcuts.exe";
};
default = {
type = "app";
program = "${build.fullBuild}/bin/komorebi.exe";
};
}
// pkgs.lib.optionalAttrs (system == "x86_64-linux") {
# the neutral scenarios against a throwaway Windows guest:
# E2E_GOLDEN=<golden image dir> nix run .#e2e-windows -- \
# OUT SCENARIO...
# KOMOREBI_E2E_BINS=<dir from build-ref.sh> tests another commit
# the golden image is built once with
# e2e/platforms/qemu_windows/build-golden.sh
e2e-windows = {
type = "app";
program = pkgs.lib.getExe e2eWindows;
};
};
devShells.default = pkgs.mkShell {
name = "komorebi";
RUST_BACKTRACE = "full";
# komorebic's build script runs on the host and wants OpenSSL, as in
# the nix build
OPENSSL_NO_VENDOR = "1";
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include";
# the Windows e2e guest (e2e/platforms/qemu_windows): the build
# scripts and the runner find OVMF here, and the vendored core
# and the platform on PYTHONPATH
OVMF_FD = "${ovmfWithTpm}";
shellHook = ''
export PYTHONPATH="$PWD/e2e/lib:$PWD/e2e''${PYTHONPATH:+:$PYTHONPATH}"
'';
# cross-compilation environment
inherit (build.msvcEnv)
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER
CC_x86_64_pc_windows_msvc
CXX_x86_64_pc_windows_msvc
AR_x86_64_pc_windows_msvc
CFLAGS_x86_64_pc_windows_msvc
CXXFLAGS_x86_64_pc_windows_msvc
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS
CARGO_BUILD_TARGET
;
packages = [
rustToolchainWithNightlyRustfmt
cargo-udeps
# cross-compilation tooling
pkgs.llvmPackages.clang-unwrapped # provides clang-cl
pkgs.llvmPackages.lld # provides lld-link
pkgs.llvmPackages.llvm # provides llvm-lib
# komorebic's build script (reqwest) runs on the build host
pkgs.pkg-config
pkgs.openssl
pkgs.cargo-deny
pkgs.cargo-nextest
pkgs.cargo-outdated
pkgs.jq
pkgs.just
pkgs.prettier
# the Windows e2e guest
pkgs.qemu
pkgs.swtpm
pkgs.xorriso
pkgs.socat
pkgs.git
(pkgs.python3.withPackages (p: [
p.pillow
p.pytest # the tests of e2e/lib
]))
];
};
pre-commit = {
check.enable = true;
settings.hooks.treefmt = {
enable = true;
package = config.treefmt.build.wrapper;
pass_filenames = false;
};
};
};
};
}
+206
View File
@@ -0,0 +1,206 @@
# What the three platforms share: the systems, nixpkgs with the rust overlay, the formatter, the
# pre-commit hook and the dev shell.
{ inputs, self, ... }:
let
lib' = import ./lib.nix { inherit inputs; };
in
{
imports = [
# pkgs of every perSystem module (a module of its own: one that takes `pkgs` cannot define it)
{
perSystem =
{ system, ... }:
{
_module.args.pkgs = lib'.mkPkgs system;
};
}
inputs.treefmt-nix.flakeModule
inputs.git-hooks-nix.flakeModule
];
# Linux and Windows (cross) builds on both Linux architectures, macOS on both Apple ones.
# x86_64-darwin uses an older nixpkgs (flake.nix, input nixpkgs-x86_64-darwin).
systems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
"x86_64-darwin"
];
flake = {
# the packages of the platform of the system the overlay is applied to: the Linux ones (overlays.linux,
# nix/linux/flake-module.nix) or the macOS ones (overlays.macos)
overlays.default =
final: prev:
(if final.stdenv.hostPlatform.isDarwin then self.overlays.macos else self.overlays.linux) final
prev;
overlays.komorebi = self.overlays.default;
};
perSystem =
{
config,
pkgs,
system,
msvcEnv,
ovmfWithTpm,
...
}:
let
crossToWindows = builtins.elem system lib'.windowsSystems;
# the toolchain of rust-toolchain.toml, with the Windows target on the systems that cross-build
rustToolchain =
let
stable = pkgs.rust-bin.fromRustupToolchainFile ../rust-toolchain.toml;
in
if crossToWindows then stable.override { targets = [ "x86_64-pc-windows-msvc" ]; } else stable;
nightlyRustfmt = pkgs.rust-bin.nightly.latest.rustfmt;
rustToolchainWithNightlyRustfmt = pkgs.symlinkJoin {
name = "rust-toolchain-with-nightly-rustfmt";
paths = [
nightlyRustfmt
rustToolchain
];
};
nightlyToolchain = pkgs.rust-bin.nightly.latest.default.override {
targets = pkgs.lib.optional crossToWindows "x86_64-pc-windows-msvc";
};
# cargo-udeps needs a nightly compiler
cargo-udeps = pkgs.writeShellScriptBin "cargo-udeps" ''
export PATH="${nightlyToolchain}/bin:$PATH"
exec ${pkgs.cargo-udeps}/bin/cargo-udeps "$@"
'';
# the tools of every platform's development
tools = [
rustToolchainWithNightlyRustfmt
cargo-udeps
# cross-compilation tooling for Windows (xwin): clang-cl, lld-link, llvm-lib
pkgs.llvmPackages.clang-unwrapped
pkgs.llvmPackages.lld
pkgs.llvmPackages.llvm
# komorebic's build script (reqwest) runs on the build host
pkgs.pkg-config
pkgs.openssl
pkgs.cargo-deny
pkgs.cargo-nextest
pkgs.cargo-outdated
pkgs.check-jsonschema
pkgs.jq
pkgs.just
pkgs.prettier
pkgs.ripgrep
# the tests of the sleep and lock signals of the KDE daemon start private buses (`kwin::power`)
pkgs.dbus
# the KDE bridge's type check and tests (`just check-js`)
pkgs.nodejs
pkgs.typescript
# the VM test library of the KDE suites
pkgs.ruff
pkgs.ty
# the Windows e2e guest (e2e/platforms/qemu_windows) and the tests of e2e/lib
pkgs.qemu
pkgs.swtpm
pkgs.xorriso
pkgs.socat
pkgs.git
(pkgs.python3.withPackages (p: [
p.pillow
p.pytest
]))
]
# publishes the schema site of the macOS tree
++ pkgs.lib.optional pkgs.stdenv.hostPlatform.isDarwin pkgs.wrangler;
mkDevShell =
{
name,
# the cross-compilation environment of the Windows build: CARGO_BUILD_TARGET makes every
# `cargo` command build for Windows, so a shell that builds the Linux or macOS packages
# natively leaves it out
cross,
}:
pkgs.mkShell (
{
inherit name;
RUST_BACKTRACE = "full";
# komorebic's build script runs on the host and wants OpenSSL, as in
# the nix build
OPENSSL_NO_VENDOR = "1";
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include";
packages = tools;
}
// pkgs.lib.optionalAttrs (crossToWindows) {
# the Windows e2e guest (e2e/platforms/qemu_windows): the build
# scripts and the runner find OVMF here, and the vendored core
# and the platform on PYTHONPATH
OVMF_FD = "${ovmfWithTpm}";
shellHook = ''
export PYTHONPATH="$PWD/e2e/lib:$PWD/e2e''${PYTHONPATH:+:$PYTHONPATH}"
'';
}
// pkgs.lib.optionalAttrs (cross && crossToWindows) {
# cross-compilation environment
inherit (msvcEnv)
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER
CC_x86_64_pc_windows_msvc
CXX_x86_64_pc_windows_msvc
AR_x86_64_pc_windows_msvc
CFLAGS_x86_64_pc_windows_msvc
CXXFLAGS_x86_64_pc_windows_msvc
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS
CARGO_BUILD_TARGET
;
}
);
in
{
treefmt = {
projectRootFile = "flake.nix";
programs = {
deadnix.enable = true;
just.enable = true;
nixfmt.enable = true;
taplo.enable = true;
rustfmt = {
enable = true;
package = nightlyRustfmt;
};
};
};
# `nix develop`: everything the three platforms need, and the Windows cross-compilation
# environment (cargo builds for x86_64-pc-windows-msvc by default). `nix develop .#native` is the
# same without it, for `cargo` commands on the Linux or macOS packages (-p komorebi-linux, ...).
devShells = {
default = mkDevShell {
name = "komorebi";
cross = true;
};
native = mkDevShell {
name = "komorebi-native";
cross = false;
};
};
pre-commit = {
check.enable = true;
settings.hooks.treefmt = {
enable = true;
package = config.treefmt.build.wrapper;
pass_filenames = false;
};
};
};
}
+65
View File
@@ -0,0 +1,65 @@
# Helpers shared by the platform modules of the flake.
{ inputs }:
rec {
# the repository, as the source of the flake
repoRoot = ./..;
# a file or directory of the repository as a store path of its own: `./tools/format-all` would name
# a place in the store path of the whole flake source, which changes with every edit of the
# repository and so would rebuild the derivation that reads it
repoFile =
rel:
builtins.path {
path = repoRoot + "/${rel}";
name = builtins.baseNameOf rel;
};
# the version of the workspace: every package says `version.workspace = true`
version =
(builtins.fromTOML (builtins.readFile (repoRoot + "/Cargo.toml"))).workspace.package.version;
# the systems that cross-build for Windows, build the Linux packages and build the macOS packages
windowsSystems = [
"x86_64-linux"
"aarch64-linux"
"aarch64-darwin"
];
linuxSystems = [
"x86_64-linux"
"aarch64-linux"
];
# Apple silicon also gets the checks of the KDE crates (they run against a fake desktop)
linuxCheckSystems = linuxSystems ++ [ "aarch64-darwin" ];
macosSystems = [
"aarch64-darwin"
"x86_64-darwin"
];
# The manifests (Cargo.toml, Cargo.lock, build scripts, .cargo/config.toml) of every member of the
# workspace, of all three platforms, and nothing else: crane makes the dummy source of the dependency
# derivations from it. A pure path (no derivation), so that crane reads it at evaluation without
# building anything, which `nix flake check --all-systems` needs on a host that cannot build for the
# other systems; and the same for every platform, so that the dependencies do not depend on the stubs.
manifestSource =
lib:
lib.fileset.toSource {
root = repoRoot;
fileset = lib.fileset.fileFilter (
file:
builtins.elem file.name [
"Cargo.toml"
"Cargo.lock"
"build.rs"
"config.toml"
]
) repoRoot;
};
# nixpkgs with the rust overlay, for a system (x86_64-darwin: see the input nixpkgs-x86_64-darwin)
mkPkgs =
system:
import (if system == "x86_64-darwin" then inputs.nixpkgs-x86_64-darwin else inputs.nixpkgs) {
inherit system;
overlays = [ (import inputs.rust-overlay) ];
};
}
+115
View File
@@ -0,0 +1,115 @@
# The crane derivations of the Linux (KDE Plasma) packages, from the Linux members of the one Cargo
# workspace (nix/workspace-src.nix).
{ inputs, self }:
let
lib' = import ../lib.nix { inherit inputs; };
inherit (lib') version;
inherit (inputs) crane;
in
{
mkKomorebiPackages =
{ pkgs }:
let
toolchain = pkgs.rust-bin.fromRustupToolchainFile ../../rust-toolchain.toml;
craneLib = (crane.mkLib pkgs).overrideToolchain toolchain;
inherit
(import ../workspace-src.nix {
inherit pkgs craneLib;
repoRoot = ../..;
platform = "linux";
})
real
src
stubbedPackages
;
lockFile = lib'.repoFile "Cargo.lock";
tomlFile = lib'.repoFile "Cargo.toml";
packages = "-p komorebi -p komorebi-linux -p komorebic -p komorebi-client -p komorebi-hotkeys";
workspaceArgs = {
pname = "komorebi-workspace-linux";
inherit version;
# the lock and the root manifest as files of their own: a path inside the filtered source
# would put all of it into the context of every derivation, and an edit of any file
# would then change the dependencies
cargoLock = lockFile;
cargoToml = tomlFile;
cargoVendorDir = craneLib.vendorCargoDeps { cargoLock = lockFile; };
};
commonArgs = workspaceArgs // {
cargoExtraArgs = "--locked ${packages}";
inherit src;
strictDeps = true;
# The version crate wants a commit at compile time. It is a constant here, so that the
# dependencies and every check are the same derivations for every commit; only the final
# build below knows the real one.
COMMIT_HASH = "nix-check";
};
# the dummy source is of the manifests alone (nix/lib.nix): the dependencies are the same
# derivations for every source file and every stub, and nothing is built at evaluation
cargoArtifacts = craneLib.buildDepsOnly (
builtins.removeAttrs commonArgs [ "src" ]
// {
dummySrc = craneLib.mkDummySrc {
src = lib'.manifestSource pkgs.lib;
cargoLock = lockFile;
};
}
);
individualCrateArgs = commonArgs // {
inherit cargoArtifacts;
doCheck = false;
doDoc = false;
};
fullBuild = craneLib.buildPackage (
individualCrateArgs
// {
COMMIT_HASH = self.rev or (pkgs.lib.removeSuffix "-dirty" (self.dirtyRev or "unknown"));
}
);
# the same build with the constant commit of the checks: the VM tests install this one, so
# that a new commit does not build the daemon again
vmBuild = craneLib.buildPackage individualCrateArgs;
extractBinaryOf =
package: binaryName:
pkgs.runCommand "komorebi-${binaryName}" { meta = package.meta // { }; } ''
mkdir -p $out/bin
cp ${package}/bin/${binaryName} $out/bin/
'';
extractBinary = extractBinaryOf fullBuild;
in
{
inherit
craneLib
src
real
workspaceArgs
stubbedPackages
packages
commonArgs
individualCrateArgs
fullBuild
vmBuild
;
komorebi = extractBinary "komorebi";
komorebic = extractBinary "komorebic";
komorebiForVm = extractBinaryOf vmBuild "komorebi";
komorebicForVm = extractBinaryOf vmBuild "komorebic";
# komorebic with the constant commit of the checks, whose output the schemas are
# checked against: it is the same for every commit
komorebicForChecks = craneLib.buildPackage (
individualCrateArgs
// {
pname = "komorebic-for-checks";
cargoExtraArgs = "--package komorebic --bin komorebic";
}
);
};
}
+526
View File
@@ -0,0 +1,526 @@
# komorebi for Linux (KDE Plasma): the native packages, the `linux-*` checks, the VM tests (`vm-*`),
# the NixOS module and the Plasma guests. The Rust packages are built on Linux; Apple silicon gets
# the checks and the VM guests' definitions, which run the same tests against the fake desktop.
{
inputs,
self,
lib,
...
}:
let
lib' = import ../lib.nix { inherit inputs; };
inherit (inputs) nixpkgs;
linux = import ./build.nix { inherit inputs self; };
inherit (lib') mkPkgs;
in
{
perSystem =
{
config,
pkgs,
system,
...
}:
let
build = linux.mkKomorebiPackages { inherit pkgs; };
isLinux = pkgs.stdenv.hostPlatform.isLinux;
# the guests install komorebi through the module, as a NixOS user does
komorebiModule = self.nixosModules.forVmTests;
# a Mac runs the guests under HVF, which is slower than KVM. A Linux host is no faster
# than a Mac to the suites that time the daemon: on a 24-core WSL2 machine, under nested
# KVM and with nothing else running, `latency` measured a p95 of 129 ms against its
# 100 ms times `scale`, where x86-1 had passed it at 1
vm = import ./tests/vm {
hostPkgs = pkgs;
inherit komorebiModule;
scale = 1.5;
};
# the probe of a NixOS-WSL2 machine (docs/linux/dev/wsl2-probe.md); the checks use a constant
# revision, so that they are the same derivations for every commit
wsl2Probe = import ./dev/wsl2-probe.nix {
inherit pkgs;
rev = self.rev or self.dirtyRev or "unknown";
};
wsl2ProbeForChecks = import ./dev/wsl2-probe.nix {
inherit pkgs;
rev = "nix-check";
};
# `run-kde` starts the demo guest of the host's architecture (docs/linux/trying-it.md). The guest's
# runner is named in the program, not depended on: the command works out what is missing
# and builds it, with its progress shown, instead of `nix run` fetching 4 GB first.
runKde =
let
arch = if system == "x86_64-linux" then "x86_64" else "aarch64";
guest = self.nixosConfigurations."run-kde-${arch}";
runner = builtins.unsafeDiscardStringContext "${guest.config.system.build.vm}";
in
pkgs.writeShellApplication {
name = "run-kde";
runtimeInputs = with pkgs; [
coreutils
e2fsprogs
gawk
git
gnugrep
gnused
mesa-demos
openssh
qemu-utils
virtiofsd
];
text = ''
: "''${RUN_KDE_GUEST:=run-kde-${arch}}"
: "''${RUN_KDE_RUNNER_BIN:=${runner}/bin/run-${guest.config.networking.hostName}-vm}"
''
+ builtins.readFile ../../scripts/linux/run-kde;
};
# rustfmt.toml uses options only a nightly rustfmt knows
nightlyRustfmt = pkgs.rust-bin.nightly.latest.rustfmt;
checkSystem = builtins.elem system lib'.linuxCheckSystems;
# PLACEHOLDER until the structure of the check sets is decided (agent flake-checks): the checks that
# evaluate a NixOS system or a VM (vm-*, the Plasma guests, the module option tests, run-kde) take
# minutes and many GB to evaluate, so `nix flake check --no-build --all-systems` cannot hold them. They
# are `legacyPackages.<system>.heavyChecks.<name>` (same names as before), not `checks.<system>`.
isHeavy =
name:
lib.hasPrefix "vm-" name
|| builtins.elem name [
"linux-nixos-module-eval"
"linux-plasma-guests-eval"
"linux-run-kde"
"linux-run-kde-doctor"
"linux-run-kde-dry-run"
];
split =
r:
if r ? allChecks then
removeAttrs r [ "allChecks" ]
// {
checks = lib.filterAttrs (n: _: !isHeavy n) r.allChecks;
legacyPackages = r.legacyPackages // {
heavyChecks = lib.filterAttrs (n: _: isHeavy n) r.allChecks;
};
}
else
r;
in
split (
lib.optionalAttrs checkSystem {
allChecks = {
# tools/format-all: the nightly rustfmt over the files of the Linux source, file by file. `cargo fmt`
# followed `mod windows;` of the shared entry files into files this source does not have.
linux-fmt =
pkgs.runCommand "komorebi-linux-fmt"
{
nativeBuildInputs = [ nightlyRustfmt ];
}
''
RUSTFMT=rustfmt bash ${lib'.repoFile "tools/format-all"} --check --root ${build.real}
touch $out
'';
linux-clippy = build.craneLib.cargoClippy (
build.individualCrateArgs
// {
cargoClippyExtraArgs = "--all-targets -- -D warnings";
}
);
linux-doc = build.craneLib.cargoDoc (
build.individualCrateArgs
// {
cargoDocExtraArgs = "--no-deps";
env.RUSTDOCFLAGS = "-D warnings";
}
);
linux-toml-fmt = build.craneLib.taploFmt (
build.workspaceArgs
// {
src = lib.sources.sourceFilesBySuffices build.real [ ".toml" ];
}
);
# cargo deny sees every member of the workspace: leave out the other platforms' packages (stubs
# here); the policy is the one deny.toml of the repository
linux-deny = build.craneLib.cargoDeny (
build.workspaceArgs
// {
inherit (build) src;
cargoDenyExtraArgs =
"--workspace " + lib.concatMapStringsSep " " (p: "--exclude ${p}") build.stubbedPackages;
cargoDenyChecks = "bans licenses sources";
}
);
linux-nextest = build.craneLib.cargoNextest (build.individualCrateArgs // { doCheck = true; });
# The bridge is not Rust: tsc checks its types from its JSDoc comments, and node runs
# its tests against a fake KWin. No package.json and no build step.
linux-bridge-js =
pkgs.runCommand "komorebi-bridge-js"
{
nativeBuildInputs = [
pkgs.nodejs
pkgs.typescript
];
src = lib.fileset.toSource {
root = ../../komorebi-linux/src/kwin;
fileset = lib.fileset.fileFilter (file: !file.hasExt "rs") ../../komorebi-linux/src/kwin;
};
}
''
cd $src
tsc -p tsconfig.json
node --test bridge.test.js
touch $out
'';
vm-lib = vm.lib;
# The schemas, the options made from one of them and the example are what the Rust types
# say: `just jsonschema` and `just nixgen-linux` write them
linux-schema-fresh =
pkgs.runCommand "komorebi-schema-fresh"
{
nativeBuildInputs = [
build.komorebicForChecks
pkgs.diffutils
];
}
''
komorebic static-config-schema | diff -u ${lib'.repoFile "schemas/linux/schema.json"} - \
|| { echo "schemas/linux/schema.json is stale: run just jsonschema"; exit 1; }
komorebic application-specific-configuration-schema | diff -u ${lib'.repoFile "schemas/linux/schema.asc.json"} - \
|| { echo "schemas/linux/schema.asc.json is stale: run just jsonschema"; exit 1; }
expected=$(sha256sum ${lib'.repoFile "schemas/linux/schema.json"} | cut -d' ' -f1)
head -n 1 ${./komorebi-options.nix} | grep -qx "# schema-sha256: $expected" \
|| { echo "nix/linux/komorebi-options.nix is stale: run just nixgen-linux"; exit 1; }
touch $out
'';
# the options the module is typed with, evaluated against the example configuration
linux-nix-options-validation = import ./tests/default.nix { inherit pkgs; };
# every file of the key binding corpus through `komorebic hotkeys compile`, as the
# module runs it
linux-keybindings-fixtures = import ./tests/keybindings.nix {
inherit pkgs;
komorebic = build.fullBuild;
};
# The NixOS module in real NixOS systems, evaluated and never built, so a Mac checks it
# too. A VM suite, `vm-module`, runs it.
linux-nixos-module-eval = import ./tests/nixos-module.nix {
inherit pkgs;
inherit (nixpkgs.lib) nixosSystem;
module = self.nixosModules.default;
komorebiFor = guestSystem: self.packages.${guestSystem}.komorebi-full;
borderEffectFor = guestSystem: self.packages.${guestSystem}.kde-rounded-corners-komorebi;
animationEffectFor = guestSystem: self.packages.${guestSystem}.kwin-effect-geometry-change-komorebi;
};
# The Plasma guests to develop against, evaluated and never built, so that every system
# sees an option that nixpkgs renamed or an assertion that fails. The QEMU guests are
# systems in a VM (`vmVariant`), with a runner for the machine that runs QEMU; UTM's is
# a system of its own. Each guest is for one kind of machine, which is also checked: the
# architecture of the guest, the machine that runs its QEMU, and that a guest of a Linux
# machine has KVM and one display device only.
linux-plasma-guests-eval =
let
guests = self.nixosConfigurations;
machines =
let
mac = {
guest = "aarch64-linux";
host = "aarch64-darwin";
};
linux = system: {
guest = system;
host = system;
};
# the runner of the demo is given its display device, its ports and whether to
# insist on KVM when it starts, by `run-kde`
demo = machine: machine // { demo = true; };
in
{
plasma-vm = mac;
plasma-vm-session = mac;
plasma-vm-x86_64 = linux "x86_64-linux";
plasma-vm-session-x86_64 = linux "x86_64-linux";
plasma-vm-aarch64 = linux "aarch64-linux";
plasma-vm-session-aarch64 = linux "aarch64-linux";
run-kde-x86_64 = demo (linux "x86_64-linux");
run-kde-aarch64 = demo (linux "aarch64-linux");
};
isDisplay =
option: lib.hasPrefix "-device virtio-vga" option || lib.hasPrefix "-device virtio-gpu" option;
qemuGuest =
name:
let
machine = machines.${name};
demo = machine.demo or false;
vm = guests.${name}.config.virtualisation.vmVariant.virtualisation;
in
assert lib.assertMsg (
guests.${name}.pkgs.stdenv.hostPlatform.system == machine.guest
) "${name} is not a ${machine.guest} guest";
assert lib.assertMsg (
vm.host.pkgs.stdenv.hostPlatform.system == machine.host
) "${name} does not run on a ${machine.host} host";
assert lib.assertMsg (
vm.qemu.forceAccel != demo
) "${name} would run without KVM or HVF, which is too slow to use";
assert lib.assertMsg (
builtins.length (builtins.filter isDisplay vm.qemu.options) == (if demo then 0 else 1)
) "${name} does not have the display devices it should have";
assert lib.assertMsg
# an x86_64 machine has a VGA of its own, which would be a second display
((machine.guest == "x86_64-linux" && !demo) == builtins.elem "-vga none" vm.qemu.options)
"${name} has `-vga none` if and only if it is an x86_64 guest with a display device";
assert lib.assertMsg (
demo -> (vm.forwardPorts == [ ] && vm.sharedDirectories.run-kde.target == "/run/run-kde")
) "${name} does not leave its ports and its shared directory to the runner's environment";
{
${name} = guests.${name}.config.virtualisation.vmVariant.system.build.toplevel;
"${name}, its runner" = guests.${name}.config.system.build.vm;
};
builds = lib.foldl' (builds: name: builds // qemuGuest name) { } (lib.attrNames machines) // {
plasma-utm = guests.plasma-utm.config.system.build.toplevel;
};
lines = lib.mapAttrsToList (
name: drv: "${name}: ${builtins.unsafeDiscardStringContext drv.drvPath}"
) builds;
in
pkgs.runCommand "plasma-guests-eval" { } ''
${lib.concatMapStringsSep "\n" (line: "echo ${lib.escapeShellArg line}") lines}
touch $out
'';
}
# The probe builds (shellcheck runs over the script and over the guest's init as it
# does) and its dry run works without a machine to probe, so that it cannot rot between
# the times somebody runs it.
// lib.optionalAttrs isLinux {
linux-wsl2-probe = wsl2ProbeForChecks.probe;
linux-wsl2-probe-dry-run =
pkgs.runCommand "wsl2-probe-dry-run" { nativeBuildInputs = [ wsl2ProbeForChecks.probe ]; }
''
wsl2-probe --dry-run --no-prompt --report report.txt > out.txt
cat out.txt
for probe in P0 P1 P2 P3 P4 P5 P6; do
grep -q "^$probe " out.txt || { echo "no verdict line for $probe"; exit 1; }
done
grep -q "PASTE THIS" out.txt
grep -q "would run" out.txt
# the report file has what the screen has
grep -q "PASTE THIS" report.txt
# a mistake in the options is refused
if wsl2-probe --nonsense 2> err.txt; then exit 1; fi
grep -q "unknown option" err.txt
touch $out
'';
}
# `run-kde` is shellchecked when it is built, and its output is what a test says it is
// lib.optionalAttrs isLinux (
lib.mapAttrs' (name: lib.nameValuePair "linux-${name}") (
{
run-kde = config.packages.run-kde;
}
// import ./tests/run-kde.nix {
inherit pkgs;
runKde = config.packages.run-kde;
}
)
)
# The VM tests need QEMU with KVM, so only Linux checks them. A Mac runs the same tests
# through legacyPackages.<system>.vm, see docs/linux/dev/vm-tests.md.
// lib.optionalAttrs isLinux (
lib.mapAttrs' (name: test: lib.nameValuePair "vm-${name}" test) vm.suites
)
# the KWin effects komorebi drives, installed and inspected without a session
// lib.optionalAttrs isLinux (
lib.mapAttrs' (name: lib.nameValuePair "linux-${name}") (
import ./tests/effects.nix {
inherit pkgs;
inherit (config) packages;
}
)
);
legacyPackages = lib.optionalAttrs checkSystem (
{
# every suite, as a test with a `driver`, on every system
vm = vm.suites;
# the suites of each tier, for the CI matrix
vmTiers = vm.tiers;
# the guests of every suite, to build on a Linux builder when the host is a Mac
vmGuests = vm.guests;
}
// lib.optionalAttrs isLinux {
# the same suites on a machine with no KVM, which is slow but works anywhere
vmTcg =
(import ./tests/vm {
hostPkgs = pkgs;
inherit komorebiModule;
scale = 8;
}).suites;
}
);
}
)
// lib.optionalAttrs (builtins.elem system lib'.linuxSystems) {
packages = {
inherit (build) komorebi komorebic;
komorebi-full = build.fullBuild;
default = build.fullBuild;
kde-rounded-corners-komorebi = pkgs.callPackage ../linux/kde-rounded-corners.nix { };
kwin-effect-geometry-change-komorebi =
pkgs.callPackage ../linux/kwin-effect-geometry-change.nix
{ };
wsl2-probe = wsl2Probe.probe;
wsl2-probe-guest = wsl2Probe.guest;
run-kde = runKde;
};
apps = {
komorebi = {
type = "app";
program = "${build.komorebi}/bin/komorebi";
meta.description = "The komorebi daemon";
};
komorebic = {
type = "app";
program = "${build.komorebic}/bin/komorebic";
meta.description = "The command line interface of komorebi";
};
wsl2-probe = {
type = "app";
program = lib.getExe wsl2Probe.probe;
meta.description = "Probes a NixOS-WSL2 machine for KVM, WSLg, a GPU and RDP";
};
run-kde = {
type = "app";
program = lib.getExe config.packages.run-kde;
meta.description = "Start the komorebi demo in a virtual machine";
};
default = config.apps.komorebi;
};
};
flake = {
overlays.linux =
final: _:
let
build = linux.mkKomorebiPackages { pkgs = mkPkgs final.stdenv.hostPlatform.system; };
in
{
inherit (build) komorebi komorebic;
komorebi-full = build.fullBuild;
# with the consumer's KWin, so that the plugin links the libkwin it is loaded into
kde-rounded-corners-komorebi = final.callPackage ./kde-rounded-corners.nix { };
kwin-effect-geometry-change-komorebi = final.callPackage ./kwin-effect-geometry-change.nix { };
};
# the same packages built with the constant commit of the checks, for the VM tests
overlays.forVmTests =
final: prev:
let
build = linux.mkKomorebiPackages { pkgs = mkPkgs final.stdenv.hostPlatform.system; };
in
(self.overlays.linux final prev)
// {
komorebi = build.komorebiForVm;
komorebic = build.komorebicForVm;
komorebi-full = build.vmBuild;
};
# `services.komorebi`, with the overlay that provides its package, so that a NixOS
# configuration needs this flake as an input and one module line (docs/linux/installation.md)
nixosModules.default = {
imports = [ ./nixos-module.nix ];
nixpkgs.overlays = [ self.overlays.linux ];
};
nixosModules.komorebi = self.nixosModules.default;
# the names of the root flake before the nested flakes were merged into it
nixosModules.komorebi-for-kde = self.nixosModules.default;
overlays.komorebi-for-kde = self.overlays.linux;
# `services.komorebi` with the packages the VM tests install
nixosModules.forVmTests = {
imports = [ ./nixos-module.nix ];
nixpkgs.overlays = [ self.overlays.forVmTests ];
};
# Plasma guests to develop against (docs/linux/dev/utm.md). For an Apple silicon Mac, QEMU runs
# the first two under HVF, and UTM boots the third from a disk image. For a Linux machine
# with KVM, QEMU runs the other four: the same two guests for an x86_64 host and for an
# aarch64 one, each of which runs the guest of its own architecture. The last two are the
# demo that `run-kde` starts.
nixosConfigurations =
let
# `guest` is the system of the guest, and `host` the pkgs of the machine that runs QEMU
mkPlasmaGuest =
{ guest, host }:
modules:
nixpkgs.lib.nixosSystem {
specialArgs.hostPkgs = host;
modules = [
self.nixosModules.default
{ nixpkgs.hostPlatform = guest; }
# `nix flake check` evaluates the toplevel of every configuration, which needs a root file
# system and a boot loader; the guests run as VMs (vmVariant), which replace both
(
{ lib, ... }:
{
fileSystems."/" = lib.mkDefault {
device = "/dev/disk/by-label/nixos";
fsType = "ext4";
};
boot.loader.grub.devices = lib.mkDefault [ "nodev" ];
}
)
]
++ modules;
};
onMac = mkPlasmaGuest {
guest = "aarch64-linux";
host = nixpkgs.legacyPackages.aarch64-darwin;
};
onLinux =
system:
mkPlasmaGuest {
guest = system;
host = nixpkgs.legacyPackages.${system};
};
session = [
./dev/plasma-vm.nix
{ komorebi.plasmaVm.session = true; }
];
# the session guest with the demo of `run-kde` (nix/linux/dev/run-kde.nix) and no display
# device, which the command gives it when it starts the guest
demo = [
./dev/plasma-vm.nix
{
komorebi.plasmaVm = {
session = true;
display = false;
};
}
./dev/run-kde.nix
({ lib, ... }: { networking.hostName = lib.mkOverride 40 "komorebi-demo"; })
];
in
{
plasma-vm = onMac [ ./dev/plasma-vm.nix ];
plasma-vm-session = onMac session;
plasma-utm = onMac [ ./dev/plasma-utm.nix ];
plasma-vm-x86_64 = onLinux "x86_64-linux" [ ./dev/plasma-vm.nix ];
plasma-vm-session-x86_64 = onLinux "x86_64-linux" session;
plasma-vm-aarch64 = onLinux "aarch64-linux" [ ./dev/plasma-vm.nix ];
plasma-vm-session-aarch64 = onLinux "aarch64-linux" session;
run-kde-x86_64 = onLinux "x86_64-linux" demo;
run-kde-aarch64 = onLinux "aarch64-linux" demo;
};
};
}
+114
View File
@@ -0,0 +1,114 @@
# The crane derivations of the macOS packages, from the macOS members of the one Cargo workspace
# (nix/workspace-src.nix).
{ inputs, self }:
let
lib' = import ../lib.nix { inherit inputs; };
inherit (lib') version;
inherit (inputs) crane;
in
{
mkKomorebiPackages =
{ pkgs }:
let
toolchain = pkgs.rust-bin.fromRustupToolchainFile ../../rust-toolchain.toml;
craneLib = (crane.mkLib pkgs).overrideToolchain toolchain;
inherit
(import ../workspace-src.nix {
inherit pkgs craneLib;
repoRoot = ../..;
platform = "macos";
})
real
src
stubbedPackages
;
lockFile = lib'.repoFile "Cargo.lock";
tomlFile = lib'.repoFile "Cargo.toml";
packages = "-p komorebi -p komorebi-macos -p komorebic -p komorebi-client -p komorebi-bar";
workspaceArgs = {
pname = "komorebi-workspace-macos";
inherit version;
# the lock and the root manifest as files of their own: a path inside the filtered source
# would put all of it into the context of every derivation, and an edit of any file
# would then change the dependencies
cargoLock = lockFile;
cargoToml = tomlFile;
cargoVendorDir = craneLib.vendorCargoDeps { cargoLock = lockFile; };
};
commonArgs = workspaceArgs // {
cargoExtraArgs = "--locked ${packages}";
inherit src;
strictDeps = true;
# a constant, so that the dependencies and every check are the same derivations for
# every commit; only fullBuild stamps the real one
COMMIT_HASH = "nix-check";
nativeBuildInputs = [
pkgs.darwin.cctools
];
postFixup = ''
for bin in $out/bin/*; do
if [ -f "$bin" ] && [ -x "$bin" ]; then
# Get all linked libraries and fix libiconv references
otool -L "$bin" | grep -o '/nix/store/[^/]*/lib/libiconv[^ ]*' | while read -r lib; do
install_name_tool -change "$lib" /usr/lib/libiconv.dylib "$bin"
done
fi
done
'';
};
# the dummy source is of the manifests alone (nix/lib.nix): the dependencies are the same
# derivations for every source file and every stub, and nothing is built at evaluation
cargoArtifacts = craneLib.buildDepsOnly (
builtins.removeAttrs commonArgs [ "src" ]
// {
dummySrc = craneLib.mkDummySrc {
src = lib'.manifestSource pkgs.lib;
cargoLock = lockFile;
};
}
);
individualCrateArgs = commonArgs // {
inherit cargoArtifacts;
doCheck = false;
doDoc = false;
};
fullBuild = craneLib.buildPackage (
individualCrateArgs
// {
COMMIT_HASH = self.rev or (pkgs.lib.removeSuffix "-dirty" (self.dirtyRev or "unknown"));
}
);
extractBinary =
binaryName:
pkgs.runCommand "komorebi-${binaryName}"
{
meta = fullBuild.meta // { };
}
''
mkdir -p $out/bin
cp ${fullBuild}/bin/${binaryName} $out/bin/
'';
in
{
inherit
craneLib
src
real
workspaceArgs
stubbedPackages
packages
individualCrateArgs
fullBuild
;
komorebi = extractBinary "komorebi";
komorebic = extractBinary "komorebic";
komorebi-bar = extractBinary "komorebi-bar";
};
}
+108
View File
@@ -0,0 +1,108 @@
# komorebi for macOS: the native packages on Apple machines, the `macos-*` checks and the
# nix-darwin module. The Rust packages cannot be built on Linux; `nix flake check --no-build
# --all-systems` evaluates them there.
{
inputs,
self,
lib,
...
}:
let
lib' = import ../lib.nix { inherit inputs; };
macos = import ./build.nix { inherit inputs self; };
inherit (lib') mkPkgs;
in
{
perSystem =
{ pkgs, system, ... }:
let
build = macos.mkKomorebiPackages { inherit pkgs; };
# rustfmt.toml uses options only a nightly rustfmt knows
nightlyRustfmt = pkgs.rust-bin.nightly.latest.rustfmt;
in
lib.optionalAttrs (builtins.elem system lib'.macosSystems) {
checks = {
macos-clippy = build.craneLib.cargoClippy build.individualCrateArgs;
# the nightly rustfmt over the macOS files only (the source of this platform has no others), with
# the 2024 edition of the manifests. `cargo fmt` would follow the `mod windows;` of the shared
# entry files (komorebi-bar, komorebi-client), which the macOS source does not have.
macos-fmt =
pkgs.runCommand "komorebi-macos-fmt"
{
nativeBuildInputs = [ nightlyRustfmt ];
}
''
RUSTFMT=rustfmt bash ${lib'.repoFile "tools/format-all"} --check --root ${build.real}
touch $out
'';
macos-toml-fmt = build.craneLib.taploFmt (
build.workspaceArgs
// {
src = lib.sources.sourceFilesBySuffices build.real [ ".toml" ];
}
);
# cargo deny sees every member of the workspace: leave out the other platforms' packages (stubs
# here); the policy is the one deny.toml of the repository
macos-deny = build.craneLib.cargoDeny (
build.workspaceArgs
// {
inherit (build) src;
cargoDenyExtraArgs =
"--workspace " + lib.concatMapStringsSep " " (p: "--exclude ${p}") build.stubbedPackages;
cargoDenyChecks = "bans licenses sources";
}
);
macos-nextest = build.craneLib.cargoNextest build.individualCrateArgs;
macos-nix-options-validation = import ./tests { inherit pkgs; };
macos-darwin-module-validation = import ./tests/darwin-module.nix { inherit pkgs; };
};
packages = {
inherit (build) komorebi komorebic komorebi-bar;
komorebi-full = build.fullBuild;
default = build.fullBuild;
};
apps = {
komorebi = {
type = "app";
program = "${build.komorebi}/bin/komorebi";
};
komorebic = {
type = "app";
program = "${build.komorebic}/bin/komorebic";
};
komorebi-bar = {
type = "app";
program = "${build.komorebi-bar}/bin/komorebi-bar";
};
default = {
type = "app";
program = "${build.fullBuild}/bin/komorebi";
};
};
};
flake = {
overlays.macos =
final: _:
let
build = macos.mkKomorebiPackages { pkgs = mkPkgs final.stdenv.hostPlatform.system; };
in
{
inherit (build) komorebi komorebic komorebi-bar;
komorebi-full = build.fullBuild;
};
darwinModules.default = import ./darwin-module.nix;
darwinModules.komorebi = import ./darwin-module.nix;
# the name of the root flake before the nested flakes were merged into it
darwinModules.komorebi-for-mac = import ./darwin-module.nix;
};
}
+286
View File
@@ -0,0 +1,286 @@
# The Windows cross build: xwin's SDK and CRT, the clang-cl/lld-link environment and the crane
# derivations of the Windows workspace (target x86_64-pc-windows-msvc), for the systems that build it.
{ inputs, self }:
let
lib' = import ../lib.nix { inherit inputs; };
inherit (lib') version;
inherit (inputs) crane;
windowsSdkVersion = "10.0.26100";
windowsCrtVersion = "14.44.17.14";
mkWindowsSdk =
pkgs:
pkgs.stdenvNoCC.mkDerivation {
name = "windows-sdk-${windowsSdkVersion}-crt-${windowsCrtVersion}";
nativeBuildInputs = [
pkgs.xwin
pkgs.cacert
];
outputHashAlgo = "sha256";
outputHashMode = "recursive";
# hash of `xwin splat --disable-symlinks`: without the flag xwin makes
# case-alias symlinks on a case-sensitive filesystem and none on a
# case-insensitive one, so the hash depends on the host
outputHash = "sha256-YjmEfoxtcmQXvUc+WtaElnyP/4osNvtSI7X6MDaNTjg=";
buildCommand = ''
export HOME=$(mktemp -d)
# keep the cache on the same filesystem as $out, as xwin moves
# files from the cache into the output and rename(2) fails with
# EXDEV when the build dir and the store are separate mounts
mkdir -p $out
xwin --accept-license \
--cache-dir $out/.xwin-cache \
--sdk-version ${windowsSdkVersion} \
--crt-version ${windowsCrtVersion} \
splat --disable-symlinks --output $out
rm -rf $out/.xwin-cache
'';
};
# the linker asks for `kernel32.lib` of a file that is `Kernel32.lib`, so
# on a case-sensitive host add a lowercase alias for every name with a
# capital in it, and the spellings the headers include each other by (the
# SDK itself is made without symlinks)
caseFolded =
{ pkgs, windowsSdk }:
if pkgs.stdenv.hostPlatform.isDarwin then
windowsSdk
else
pkgs.runCommandLocal "${windowsSdk.name}-case-folded" { } ''
cp -rs ${windowsSdk} $out
chmod -R u+w $out
cd $out
find . -name '*[A-Z]*' | while read -r path; do
directory=$(dirname "$path")
name=$(basename "$path")
lower=$(echo "$name" | tr 'A-Z' 'a-z')
if [ ! -e "$directory/$lower" ] && [ ! -L "$directory/$lower" ]; then
ln -s "$name" "$directory/$lower"
fi
done
# headers include each other with spellings that differ from the
# file name (`DriverSpecs.h` for `driverspecs.h`), which xwin
# covers with symlinks unless told not to
grep -Rhoi -E '^[[:space:]]*#[[:space:]]*include[[:space:]]*[<"][^>"]+[>"]' \
crt/include sdk/include 2>/dev/null \
| sed -E 's/.*[<"]([^>"]+)[>"]/\1/; s#\\#/#g; s#.*/##' \
| sort -u > $TMPDIR/spellings
find crt/include sdk/include \( -type f -o -type l \) \
| awk -v spellings=$TMPDIR/spellings '
BEGIN {
while ((getline line < spellings) > 0)
known[tolower(line)] = known[tolower(line)] "\n" line
}
{
n = split($0, parts, "/"); base = parts[n]
dir = substr($0, 1, length($0) - length(base))
m = split(known[tolower(base)], names, "\n")
for (i = 1; i <= m; i++)
if (names[i] != "" && names[i] != base)
print dir "\t" base "\t" names[i]
}' \
| while IFS=$'\t' read -r directory name spelling; do
if [ ! -e "$directory$spelling" ] && [ ! -L "$directory$spelling" ]; then
ln -s "$name" "$directory$spelling"
fi
done
'';
mkMsvcEnv =
{ pkgs, windowsSdk }:
let
sdk = caseFolded { inherit pkgs windowsSdk; };
clangVersion = pkgs.lib.versions.major pkgs.llvmPackages.clang.version;
in
{
# linker for the windows target
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER = "lld-link";
# c/c++ compiler
CC_x86_64_pc_windows_msvc = "clang-cl";
CXX_x86_64_pc_windows_msvc = "clang-cl";
AR_x86_64_pc_windows_msvc = "llvm-lib";
# IMPORTANT: libclang include path MUST come first to avoid header conflicts
CFLAGS_x86_64_pc_windows_msvc = builtins.concatStringsSep " " [
"--target=x86_64-pc-windows-msvc"
"-Wno-unused-command-line-argument"
"-fuse-ld=lld-link"
"/imsvc${pkgs.llvmPackages.libclang.lib}/lib/clang/${clangVersion}/include"
"/imsvc${sdk}/crt/include"
"/imsvc${sdk}/sdk/include/ucrt"
"/imsvc${sdk}/sdk/include/um"
"/imsvc${sdk}/sdk/include/shared"
];
CXXFLAGS_x86_64_pc_windows_msvc = builtins.concatStringsSep " " [
"--target=x86_64-pc-windows-msvc"
"-Wno-unused-command-line-argument"
"-fuse-ld=lld-link"
"/imsvc${pkgs.llvmPackages.libclang.lib}/lib/clang/${clangVersion}/include"
"/imsvc${sdk}/crt/include"
"/imsvc${sdk}/sdk/include/ucrt"
"/imsvc${sdk}/sdk/include/um"
"/imsvc${sdk}/sdk/include/shared"
];
# target-specific rust flags with linker flavor and library search paths
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS = builtins.concatStringsSep " " [
"-Clinker-flavor=lld-link"
"-Lnative=${sdk}/crt/lib/x86_64"
"-Lnative=${sdk}/sdk/lib/um/x86_64"
"-Lnative=${sdk}/sdk/lib/ucrt/x86_64"
];
# cargo target
CARGO_BUILD_TARGET = "x86_64-pc-windows-msvc";
};
mkKomorebiPackages =
{
pkgs,
windowsSdk,
# the commit stamped into the binaries; a build for the e2e guest
# gives a constant one, so that a new commit does not rebuild it
commit ? self.rev or (pkgs.lib.removeSuffix "-dirty" (self.dirtyRev or "unknown")),
}:
let
# toolchain with windows msvc target
toolchain = (pkgs.rust-bin.fromRustupToolchainFile ../../rust-toolchain.toml).override {
targets = [ "x86_64-pc-windows-msvc" ];
};
craneLib = (crane.mkLib pkgs).overrideToolchain toolchain;
msvcEnv = mkMsvcEnv { inherit pkgs windowsSdk; };
# the Windows members of the one Cargo workspace; the Linux and macOS members are stubs
# (nix/workspace-src.nix), so that an edit of one changes nothing here
inherit
(import ../workspace-src.nix {
inherit pkgs craneLib;
repoRoot = ../..;
platform = "windows";
})
real
src
stubbedPackages
;
lockFile = lib'.repoFile "Cargo.lock";
tomlFile = lib'.repoFile "Cargo.toml";
workspaceArgs = {
pname = "komorebi-workspace-windows";
inherit version;
# the lock and the root manifest as files of their own: a path inside the filtered source would put the
# whole filtered source into the context of every derivation, and then an edit of any file
# would change the dependencies
cargoLock = lockFile;
cargoToml = tomlFile;
cargoVendorDir = craneLib.vendorCargoDeps { cargoLock = lockFile; };
};
commonArgs = workspaceArgs // {
inherit src;
strictDeps = true;
# applications.json is committed now; this copies the pinned asc input over it, so that the
# embedded file is the pinned one even when the committed copy is older
preBuild = "mkdir -p komorebic && cp ${inputs.asc}/applications.json komorebic/applications.json";
# a constant, so that the dependencies and every check are the same
# derivations for every commit; only fullBuild stamps the real one
COMMIT_HASH = "nix-check";
# build inputs for cross-compilation
nativeBuildInputs = [
pkgs.llvmPackages.clang-unwrapped
pkgs.llvmPackages.lld
pkgs.llvmPackages.llvm
# komorebic build script uses reqwest, which runs on the build host
pkgs.pkg-config
pkgs.openssl
pkgs.openssl.dev
];
# point the host-side openssl-sys build at the nix openssl
OPENSSL_NO_VENDOR = "1";
OPENSSL_LIB_DIR = "${pkgs.openssl.out}/lib";
OPENSSL_INCLUDE_DIR = "${pkgs.openssl.dev}/include";
# cross-compilation environment
inherit (msvcEnv)
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER
CC_x86_64_pc_windows_msvc
CXX_x86_64_pc_windows_msvc
AR_x86_64_pc_windows_msvc
CFLAGS_x86_64_pc_windows_msvc
CXXFLAGS_x86_64_pc_windows_msvc
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_RUSTFLAGS
CARGO_BUILD_TARGET
;
};
# the dummy source is of the manifests alone (nix/lib.nix): the dependencies are the same
# derivations for every source file and every stub, and nothing is built at evaluation
cargoArtifacts = craneLib.buildDepsOnly (
builtins.removeAttrs commonArgs [ "src" ]
// {
dummySrc = craneLib.mkDummySrc {
src = lib'.manifestSource pkgs.lib;
cargoLock = lockFile;
};
}
);
individualCrateArgs = commonArgs // {
inherit cargoArtifacts;
doCheck = false;
doDoc = false;
};
fullBuild = craneLib.buildPackage (
individualCrateArgs
// {
pname = "komorebi-workspace";
COMMIT_HASH = commit;
}
);
extractBinary =
binaryName:
pkgs.runCommand "komorebi-${binaryName}"
{
meta = fullBuild.meta // { };
}
''
mkdir -p $out/bin
cp ${fullBuild}/bin/${binaryName}.exe $out/bin/
'';
in
{
inherit
craneLib
src
real
workspaceArgs
stubbedPackages
individualCrateArgs
fullBuild
msvcEnv
;
komorebi = extractBinary "komorebi";
komorebic = extractBinary "komorebic";
komorebic-no-console = extractBinary "komorebic-no-console";
komorebi-bar = extractBinary "komorebi-bar";
komorebi-gui = extractBinary "komorebi-gui";
komorebi-shortcuts = extractBinary "komorebi-shortcuts";
komorebi-testwin = extractBinary "komorebi-testwin";
komorebi-e2e-agent = extractBinary "komorebi-e2e-agent";
};
in
{
inherit mkWindowsSdk mkMsvcEnv mkKomorebiPackages;
}
+201
View File
@@ -0,0 +1,201 @@
# komorebi for Windows, cross-built with xwin (x86_64-pc-windows-msvc): the `windows-*` packages,
# apps and checks, and the Windows e2e guest. Built on Linux (and on Apple silicon); the binaries run
# on Windows only.
{
inputs,
self,
lib,
...
}:
let
lib' = import ../lib.nix { inherit inputs; };
windows = import ./build.nix { inherit inputs self; };
in
{
perSystem =
{ pkgs, system, ... }:
let
windowsSdk = windows.mkWindowsSdk pkgs;
build = windows.mkKomorebiPackages { inherit pkgs windowsSdk; };
# the binaries the e2e guest runs: the same build, but with a
# constant commit stamp, so a new commit (or a dirty tree) that does
# not change a source file reuses it. The commit under test is
# KOMOREBI_E2E_COMMIT, not what the binary says.
e2eBuild = windows.mkKomorebiPackages {
inherit pkgs windowsSdk;
commit = "e2e";
};
# OVMF without SMM and with the TPM support Windows 11 Setup asks for
ovmfWithTpm = (pkgs.OVMF.override { tpmSupport = true; }).fd;
e2eWindows = pkgs.writeShellApplication {
name = "e2e-windows";
runtimeInputs = [
pkgs.qemu
pkgs.swtpm
(pkgs.python3.withPackages (p: [ p.pillow ]))
];
text = ''
: "''${E2E_GOLDEN:?set E2E_GOLDEN to the golden image directory}"
out=''${1:?usage: e2e-windows OUT SCENARIO...}
shift
# the binaries under test: this checkout's, or those of another
# commit, made by e2e/platforms/qemu_windows/build-ref.sh
if [ -n "''${KOMOREBI_E2E_BINS:-}" ]; then
bins=$KOMOREBI_E2E_BINS
if [ -f "$bins/commit" ]; then
KOMOREBI_E2E_COMMIT=$(cat "$bins/commit")
fi
else
bins=$(mktemp -d)
trap 'rm -rf "$bins"' EXIT
cp ${e2eBuild.komorebi}/bin/komorebi.exe "$bins"
cp ${e2eBuild.komorebic}/bin/komorebic.exe "$bins"
cp ${e2eBuild.komorebi-testwin}/bin/komorebi-testwin.exe "$bins"
cp ${e2eBuild.komorebi-bar}/bin/komorebi-bar.exe "$bins"
fi
export OVMF_FD=${ovmfWithTpm}
export KOMOREBI_E2E_BAR_SCHEMA=${lib'.repoFile "schemas/windows/schema.bar.json"}
export KOMOREBI_E2E_COMMIT=''${KOMOREBI_E2E_COMMIT:-${self.rev or "dirty"}}
export PYTHONPATH=${lib'.repoFile "e2e/lib"}:${lib'.repoFile "e2e"}
exec python -m platforms.qemu_windows.run \
"$E2E_GOLDEN" "$bins" "$out" "$@"
'';
};
# the nightly rustfmt of the checks is the one of the dev shell: see nix/dev-shell.nix
nightlyRustfmt = pkgs.rust-bin.nightly.latest.rustfmt;
in
{
# the dev shell (nix/dev-shell.nix) cross-compiles with these
_module.args = {
inherit windowsSdk ovmfWithTpm;
inherit (build) msvcEnv;
};
}
// lib.optionalAttrs (builtins.elem system lib'.windowsSystems) {
checks = {
windows-clippy = build.craneLib.cargoClippy (
build.individualCrateArgs
// {
cargoClippyExtraArgs = "--all-targets -- -D warnings";
}
);
# the nightly rustfmt over the Windows files, as tools/format-all does locally. Not `cargo fmt`:
# it runs the stable rustfmt (which ignores the nightly options of rustfmt.toml) and follows
# `mod linux;` into files the Windows source does not have.
windows-fmt =
pkgs.runCommand "komorebi-windows-fmt"
{
nativeBuildInputs = [ nightlyRustfmt ];
}
''
RUSTFMT=rustfmt bash ${lib'.repoFile "tools/format-all"} --check --root ${build.real}
touch $out
'';
windows-toml-fmt = build.craneLib.taploFmt (
build.workspaceArgs
// {
src = pkgs.lib.sources.sourceFilesBySuffices build.real [ ".toml" ];
}
);
# cargo deny sees every member of the workspace: leave out the Linux and macOS packages, which
# are stubs here and have their own checks (linux-deny, macos-deny)
windows-deny = build.craneLib.cargoDeny (
build.workspaceArgs
// {
inherit (build) src;
cargoDenyExtraArgs =
"--workspace " + pkgs.lib.concatMapStringsSep " " (p: "--exclude ${p}") build.stubbedPackages;
}
);
windows-nextest = build.craneLib.cargoNextest build.individualCrateArgs;
}
// {
# the Python of the Windows e2e guest that needs no guest: the
# vendored core's tests and those of the runner
windows-e2e-tests =
pkgs.runCommand "windows-e2e-tests"
{
nativeBuildInputs = [
pkgs.git
(pkgs.python3.withPackages (p: [
p.pillow
p.pytest
]))
];
}
''
cp -r ${lib'.repoFile "e2e"} e2e
chmod -R u+w e2e
cd e2e
export HOME=$TMPDIR PYTHONPATH=$PWD/lib:$PWD
python -m pytest -q -p no:cacheprovider lib/tests platforms/qemu_windows/tests
touch $out
'';
};
packages = {
windows-komorebi = build.komorebi;
windows-komorebic = build.komorebic;
windows-komorebic-no-console = build.komorebic-no-console;
windows-komorebi-bar = build.komorebi-bar;
windows-komorebi-gui = build.komorebi-gui;
windows-komorebi-shortcuts = build.komorebi-shortcuts;
windows-komorebi-testwin = build.komorebi-testwin;
windows-komorebi-e2e-agent = build.komorebi-e2e-agent;
windows-sdk = windowsSdk;
# the test window of the Windows e2e guest, built with the constant
# stamp: a new commit does not rebuild what it depends on
windows-komorebi-testwin-e2e = e2eBuild.komorebi-testwin;
windows-komorebi-full = build.fullBuild;
windows-default = build.fullBuild;
};
apps = {
windows-komorebi = {
type = "app";
program = "${build.komorebi}/bin/komorebi.exe";
};
windows-komorebic = {
type = "app";
program = "${build.komorebic}/bin/komorebic.exe";
};
windows-komorebic-no-console = {
type = "app";
program = "${build.komorebic-no-console}/bin/komorebic-no-console.exe";
};
windows-komorebi-bar = {
type = "app";
program = "${build.komorebi-bar}/bin/komorebi-bar.exe";
};
windows-komorebi-gui = {
type = "app";
program = "${build.komorebi-gui}/bin/komorebi-gui.exe";
};
windows-komorebi-shortcuts = {
type = "app";
program = "${build.komorebi-shortcuts}/bin/komorebi-shortcuts.exe";
};
windows-default = {
type = "app";
program = "${build.fullBuild}/bin/komorebi.exe";
};
}
// lib.optionalAttrs (system == "x86_64-linux") {
# the neutral scenarios against a throwaway Windows guest:
# E2E_GOLDEN=<golden image dir> nix run .#e2e-windows -- \
# OUT SCENARIO...
# KOMOREBI_E2E_BINS=<dir from build-ref.sh> tests another commit
# the golden image is built once with
# e2e/platforms/qemu_windows/build-golden.sh
e2e-windows = {
type = "app";
program = pkgs.lib.getExe e2eWindows;
};
};
};
}
+172 -16
View File
@@ -1,22 +1,168 @@
# The source of a build from the one Cargo workspace of this repository.
# The source of a build of one platform, cut from the one Cargo workspace of this repository.
#
# Cargo has to load every member of the workspace, but a build of one platform needs the sources of
# that platform's crates only. `real` is the filtered source (the platform's crates, the crates of the
# root workspace they depend on, the root manifest and lock); every other member (`isReal` is false)
# is added as its own manifest with an empty library and a `main` that does nothing. An edit of a
# crate of another platform then changes nothing here, and an edit of another platform's manifest
# changes only the stub copy of it.
# import ./workspace-src.nix { pkgs; craneLib; repoRoot; platform = "windows" | "linux" | "macos"; }
#
# workspaceSrc { pkgs; repoRoot; real; isReal = member: bool; }
# Cargo has to load every member of the workspace (the root manifest lists the members of all three
# platforms), but a build of one platform needs the sources of that platform's crates only. The result:
#
# real the filtered source: the members of the platform, the root manifest and lock, the
# platform's files of the crates the platforms share, and the data files they read
# src `real` plus, for every other member, its own manifest with an empty library and a `main`
# that does nothing (a stub: cargo refuses a workspace with a member that is not there, and
# a lock file that has packages the workspace does not have fails under --locked)
# members the members of the platform; stubbed the others; stubbedPackages their package names (for
# `cargo deny --exclude`)
#
# An edit under one platform therefore changes nothing in the derivations of the others, except where
# the edit is in a crate they share or in the root manifest, lock or toolchain files.
{
pkgs,
craneLib,
repoRoot,
real,
isReal,
platform,
}:
let
inherit (pkgs) lib;
members = (builtins.fromTOML (builtins.readFile "${real}/Cargo.toml")).workspace.members;
root = toString repoRoot;
allMembers = (builtins.fromTOML (builtins.readFile (repoRoot + "/Cargo.toml"))).workspace.members;
# the crates of the root workspace that the KDE packages depend on, by path (nix/linux/root-crates.json,
# kept equal to `cargo tree` by tools/kde-closure-check); komorebi-bar and komorebi-bar-core (the egui
# bar) are not in it, so an edit of the bar does not rebuild the KDE packages
linuxShared = builtins.fromJSON (builtins.readFile ./linux/root-crates.json) ++ [
"komorebi-daemon"
];
linuxOnly = [
"komorebi-linux"
"komorebi-hotkeys"
];
macosOnly = [ "komorebi-macos" ];
membersOf = {
# everything but the other two platforms' daemons
windows = builtins.filter (m: !(builtins.elem m (linuxOnly ++ macosOnly))) allMembers;
linux = linuxOnly ++ linuxShared;
macos =
macosOnly
++ [
"komorebic"
"komorebi-client"
"komorebi-bar"
"komorebi-daemon"
"komorebi-layouts"
"komorebi-scrolling"
"komorebi-themes"
]
++ builtins.filter (m: lib.hasPrefix "crates/" m) allMembers;
};
members = builtins.filter (m: builtins.elem m membersOf.${platform}) allMembers;
stubbed = builtins.filter (m: !(builtins.elem m members)) allMembers;
stubbedPackages = map (
m: (builtins.fromTOML (builtins.readFile (repoRoot + "/${m}/Cargo.toml"))).package.name
) stubbed;
# the crates one entry point serves for every platform: each keeps its own platform's modules and
# tests (named by prefix) and drops the other platforms'
sharedCrates = [
"komorebic"
"komorebi-client"
"komorebi-bar"
"komorebi-daemon"
];
markers = {
windows = "(src/windows(\\.rs|/.*)|tests/windows_.*)";
linux = "(src/linux(\\.rs|/.*)|tests/linux_.*)";
macos = "(src/macos(\\.rs|/.*)|tests/macos_.*)";
};
otherMarkers = builtins.attrValues (removeAttrs markers [ platform ]);
ofOtherPlatform =
rel:
builtins.any (
crate:
lib.hasPrefix "${crate}/" rel
&& builtins.any (
marker: builtins.match marker (lib.removePrefix "${crate}/" rel) != null
) otherMarkers
) sharedCrates;
# the directories of the source (a file is kept only under one of them)
roots =
members
++ [
"docs"
"schemas"
".cargo"
]
# the configurations that the config_parity test of komorebi-linux loads
++ lib.optional (platform == "linux") "spikes/config-parity";
underRoot = rel: builtins.any (r: rel == r || lib.hasPrefix "${r}/" rel) roots;
# a directory above a root (`crates` for `crates/komorebi-core`)
aboveRoot = rel: builtins.any (r: lib.hasPrefix "${rel}/" r) roots;
# data files that the Rust sources and tests read, outside the Cargo sources
extra =
rel:
# the test inputs of every crate
(builtins.match ".*/(tests|fixtures|golden)/.*" rel != null)
|| {
windows =
# the whole of docs/ (they are embedded in komorebic and the bar) except the other platforms' and
# the developer notes; the macOS CLI pages are read by a test of komorebic-core
(
lib.hasPrefix "docs/" rel
&& !(builtins.any (p: lib.hasPrefix p rel) [
"docs/linux/"
"docs/macos/"
"docs/dev/"
])
)
|| lib.hasPrefix "docs/macos/cli/" rel
|| lib.hasPrefix "schemas/" rel
|| lib.hasPrefix "komorebic/fixtures/" rel;
linux =
# the script komorebi loads into KWin and the JSON the tests and the daemon read
(lib.hasSuffix ".js" rel || lib.hasSuffix ".json" rel)
&& !(lib.hasPrefix "docs/" rel || lib.hasPrefix "schemas/" rel)
|| lib.hasPrefix "schemas/linux/" rel
|| lib.hasPrefix "spikes/config-parity/" rel
|| builtins.elem rel [
# the example configuration and the sample key bindings that `komorebic quickstart` includes
"docs/linux/komorebi.example.json"
"docs/linux/skhdrc.sample"
# the page that shows the example configuration, which a test compares with the file
"docs/linux/example-configurations.md"
# the example a test loads, to see that the docs show one that works
"docs/linux/usage/wallpapers.md"
];
macos =
lib.hasSuffix ".json" rel && !(lib.hasPrefix "docs/" rel || lib.hasPrefix "schemas/" rel)
|| lib.hasPrefix "docs/macos/" rel
|| lib.hasPrefix "schemas/macos/" rel
|| lib.hasPrefix "komorebic/fixtures/" rel;
}
.${platform};
real = lib.cleanSourceWith {
src = repoRoot;
filter =
path: type:
let
rel = lib.removePrefix "${root}/" path;
isRootFile = !(lib.hasInfix "/" rel);
in
if type == "directory" then
rel == root || aboveRoot rel || (underRoot rel && !(ofOtherPlatform rel))
else
# the files at the top: Cargo.toml, Cargo.lock, rust-toolchain.toml, rustfmt.toml, deny.toml
(isRootFile && craneLib.filterCargoSources path type)
|| (
underRoot rel && !(ofOtherPlatform rel) && ((craneLib.filterCargoSources path type) || extra rel)
);
};
stub = member: ''
mkdir -p $out/${member}/src
cp ${
@@ -28,9 +174,19 @@ let
: > $out/${member}/src/lib.rs
echo 'fn main() {}' > $out/${member}/src/main.rs
'';
src = pkgs.runCommand "komorebi-${platform}-src" { } ''
cp -r ${real} $out
chmod -R u+w $out
${lib.concatMapStrings stub stubbed}
'';
in
pkgs.runCommand "komorebi-workspace-src" { } ''
cp -r ${real} $out
chmod -R u+w $out
${lib.concatMapStrings stub (builtins.filter (m: !(isReal m)) members)}
''
{
inherit
real
src
members
stubbed
stubbedPackages
;
}