🏗️ 我的Nix/NixOS配置详细文档正在施工中,完成进度:76/165 🏗️
为了更好的文档阅读体验,请看GitHub Pages的版本。
目录
Nix/NixOS简介
Nix是一个可重现的(Reproducible)包管理器,也是一门纯函数式的(Pure Functional)编程语言。 理论上讲,Nix包管理器可以安装在任何Linux发行版上(比如Ubuntu、Debian和Arch等),并与这些发行版原有的包管理器(比如snap、apt和pacman等)共存。 而NixOS则是完全由Nix包管理器进行管理的Linux发行版。
Nix/NixOS采用了“包(Package)”的理念,将Linux内核、驱动、函数库、用户程序、配置文件等方方面面抽象出来。 这类似于Linux内核将设备、磁盘文件、管道等都抽象为文件的理念。 这样的抽象使得Nix/NixOS能以一种统一的方式管理所有的包。
为了防止“包”被用户有意或无意地修改(即保证可重现性),Nix/NixOS将所有的包都放在一个只读文件系统中(挂载在/nix/store
目录上)。
这个目录中的包仅能通过Nix包管理器和Nix语言编程进行增删改。
为了将/nix/store
中的文件放置在它们应该在的地方(比如某个用户使用的包里存在bin/
目录,则应当把bin/中所有的文件放入/run/current-system/sw/bin/
),Nix/NixOS大量使用符号链接。
上述Nix/NixOS的特点和传统Linux发行版有着极大的区别。 这使得Nix/NixOS的学习曲线十分陡峭。 不过当你适应Nix/NixOS的这些特点后,它可以极大提升工程效率!
xieby1和Nix/NixOS
多年来,Nix/NixOS已成为我学习和工作的重要基础,主要用于以下方面:
- 学习Linux。 Nix/NixOS采用了“包(Package)”的理念,将Linux内核、驱动、函数库、用户程序、配置文件等方方面面抽象出来。 通过学习Nix语言,我能够以统一的方式了解Linux系统的各个方面,这是其他工具所无法提供的。
- 管理环境。 通过使用nix-shell管理所有依赖(包括库、环境变量、配置等),可以避免项目环境重现的问题。 类似的工具还有虚拟机和Docker。 相比使用虚拟机,它更轻量。 相比Docker,它支持可复现构建,采用Nix语言更灵活。 详细可见我于2023年为实验室同学们准备的推荐Nix/NixOS的幻灯片。
- 备份电脑。 Nix/NixOS能够管理系统、软件及其配置。 虽然Nix/NixOS不直接管理数据,但Nix/NixOS可以很好地管理数据同步软件,比如Syncthing。 因此只要保留着Nix/NixOS的配置文件(由Nix语言编写),就能恢复出一个几乎一模一样1的软件环境/操作系统。
xieby1的Nix/NixOS配置
这个仓库Github: xieby1/nix_config里存放着我的Nix/NixOS配置和文档。 该仓库使用nix expression,而非nix flakes; 使用NixOS稳定源(目前版本25.05),而非非稳定源(unstable)。 该仓库的配置在多个平台都可以正常使用:
- NixOS: QEMU✅,NixOS单系统✅,NixOS+Windows双系统✅
- Nix: Linux✅,安卓(nix-on-droid)✅,WSL2✅
你可以使用该仓库的配置,配置出完整NixOS操作系统。 也可以使用其中的部分包、模块,扩充自己的Nix/NixOS。 若你不仅只是想安装Nix/NixOS,还想了解更多Nix/NixOS的知识, 欢迎看看这个仓库的文档xieby1.github.io/nix_config。
文件夹结构
- docs/: 文档
- scripts/: nix脚本
- fhs-shell/: 采用FHS的nix-shell脚本
- shell/: nix-shell脚本
- pkgs/: 独立的软件包脚本
- sys/: 系统总体配置(nixos-rebuild的配置)
- cli/: 系统命令行配置
- gui/: 系统图形配置
- modules/: 系统模块
- usr/: 用户总体配置(home-manager的配置)
- cli/: 用户命令行配置
- gui/: 用户图形配置
- modules/: 用户模块
- nix-on-droid.nix: 安卓总体配置(nix-on-droid的配置)
- modules/: nixos/home-manager通用的模块
nix脚本的使用方法
安装Nix不在此赘述,参考nixos.org/download.html。
安装完Nix后,下载所需的nix脚本,然后:
fhs-shell/
和shell/
脚本用nix-shell
命令进入shell环境;pkgs/
脚本用nix-build
命令生成软件包。
nix-shell的例子
# 以xiangshan.nix配置香山开发环境为例
# 进入香山的根目录
cd Xiangshan
# 下载xiangshan.nix脚本,并重命名为shell.nix
wget https://raw.githubusercontent.com/xieby1/nix_config/main/scripts/shell/xiangshan.nix -O shell.nix
# 进入nix shell
nix-shell
nix配置的使用方法
虚拟机/物理机单系统/物理机双系统 安装NixOS 可以参考我两年前的NixOS安装过程。 安装Nix/NixOS不在此赘述,参考nixos.org/download.html。
安装完Nix/NixOS后,首先下载我的配置
git clone https://github.com/xieby1/nix_config.git ~/.config/nixpkgs
# [仅NixOS] 在imports中添加sys/的路径
vim /etc/nixos/configuration.nix
然后设置软件源,在NixOS中推荐使用sudo
。
- 注一:更多其他nix channels参考 NixOS Wiki: Nix channels 和Nix channel status。
- 注二:为什么用https://nixos.org/channels/nixos-25.05,
而非https://github.com/NixOS/nixpkgs/archive/release-25.05.tar.gz?
前者包含额外内容,比如programs.command-not-found.dbPath,详细见
man configuration.nix
。
# [对于NixOS]
nix-channel --add https://nixos.org/channels/nixos-25.05 nixos
# [对于Nix]
nix-channel --add https://nixos.org/channels/nixos-25.05 nixpkgs
# 添加home manager源
nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.05.tar.gz home-manager
nix-channel --update
最后部署配置
# [仅NixOS]
sudo nixos-rebuild switch
# 安装home-manager
nix-shell '<home-manager>' -A install
# 在home-manager配置中添加usr/的路径
vim ~/.config/home-manager/home.nix
## 例如 imports = [ /home/xieby1/.config/nixpkgs/usr ];
# 用nix expression而不使用nix flakes
rm ~/.config/home-manager/flake.nix
home-manager switch
引用
nix 2.8的impure特性和home-manager等会引入一些你几乎察觉不到的差异。
Dolstra, Eelco. “The purely functional software deployment model.” (2006).
github.com/t184256/nix-on-droid termux的分支,支持nix。
config.nix
当初始化nixpkgs
时,例如pkgs = import <nixpkgs> {}
,
nixpkgs
的初始化代码会读取~/.config/nixpkgs/config.nix
作为nixpkgs.config
参数。
如果你对nixpkgs
的初始化感兴趣,可以去看看这部分的源代码<nixpkgs>/pkgs/top-level/impure.nix
。
config.nix
文件(即nixpkgs.config
)接受的参数可以参考nixpkgs的官方文档的
config Options Reference章节,
或是去看nixpkgs这部分的源码<nixpkgs>/pkgs/top-level/config.nix
。
下面是添加了注解的我的config.nix
:
{
禁用安装非本地的包,比如禁止x86_64-linux的包被安装到aarch64-linux上。
allowUnsupportedSystem = false;
allowUnfree = true;
packageOverrides = pkgs: rec {
添加nix user repository (NUR)到nixpkgs里。
nur = import (builtins.fetchTarball "https://github.com/nix-community/NUR/archive/master.tar.gz") {
pkgs = pkgsu;
};
添加非稳定版的nixpkgs到nixpkgs里,
比如非稳定版的hello可以通过pkgs.pkgsu.hello
来访问。
pkgsu = import (builtins.fetchTarball "https://github.com/NixOS/nixpkgs/archive/master.tar.gz") {};
添加flake-compat,用于在nix expression中使用flake的包
flake-compat = import (builtins.fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/0f9255e01c2351cc7d116c072cb317785dd33b33.tar.gz";
sha256 = "0m9grvfsbwmvgwaxvdzv6cmyvjnlww004gfxjvcl806ndqaxzy4j";
});
};
}
TODO: nix/nix.conf
opt.nix
system.nix
sys/cli
本文件是NixOS的CLI配置。 主要包含两部分内容:
- 系统环境管理(root的环境)
- 系统配置
{ config, pkgs, ... }:
{
系统环境管理(root的环境)
# List packages installed in system profile. To search, run:
# $ nix search wget
environment.systemPackages = with pkgs; [
git
file
wget
fzf
linuxPackages.perf
];
# neovim
programs.neovim.enable = true;
programs.neovim.defaultEditor = true;
programs.neovim.viAlias = true;
programs.neovim.vimAlias = true;
# ssh
services.openssh = {
enable = true;
settings.X11Forwarding = true;
};
系统配置
启用NTFS文件系统的支持。 如此就可以在NixOS/Windows双系统的电脑上挂在Windows的分区啦。
boot.supportedFilesystems = [ "ntfs" ];
启用podman。 podman是一个十分好用的docker实现。 支持用户态容器(不需要sudo),可以方便地挂在容器镜像的文件系统。
virtualisation.podman.enable = true;
配置binfmt,让非本地指令集的用户程序可以正常运行。
比如在x86_64-linux上运行aarch64-linux的用户程序。
注意:不能在配和本地一样的binfmt,比如不能在x86_64-linux的机器上配置x86_64-linux的binfmt。
不然会出现奇怪的嵌套?你执行任何一条命令(x86_64-linux)都需要去调用qemu-x86_64,
但qemu-x86_64本事也是x86_64-linux的,所以会死循环?
我做了个实验:在x86_64-linux的NixOS中启用x86_64-linux的binfmt。
任何程序都执行不了了,连关机都不行,只能强制重启。
不过好在NixOS可以回滚,轻松复原实验前的环境。
下面的filterAttrs
就是用来保证不配置本地的binfmt。
boot.binfmt.registrations = pkgs.lib.filterAttrs (n: v: n!=builtins.currentSystem) {
x86_64-linux = {
interpreter = "${pkgs.pkgsStatic.qemu-user}/bin/qemu-x86_64";
magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00'';
mask = ''\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
wrapInterpreterInShell = false;
};
aarch64-linux = {
interpreter = "${pkgs.pkgsStatic.qemu-user}/bin/qemu-aarch64";
magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xb7\x00'';
mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\x00\xff\xfe\xff\xff\xff'';
wrapInterpreterInShell = false;
};
riscv64-linux = {
interpreter = "${pkgs.pkgsStatic.qemu-user}/bin/qemu-riscv64";
magicOrExtension = ''\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00'';
mask = ''\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff'';
wrapInterpreterInShell = false;
};
};
启用docdev。 在home-manager中装devdoc似乎有问题,得在NixOS中装才行。 之后有空再来详细研究。
# Make sure devdoc outputs are installed.
documentation.dev.enable = true;
# Make sure legacy path is installed as well.
environment.pathsToLink = [ "/share/gtk-doc" ];
启用ADB,安卓搞事情必备。
programs.adb.enable = true;
users.users.xieby1.extraGroups = ["adbusers"];
nix.settings.trusted-users = ["root" "xieby1"];
zramSwap.enable = true;
boot.kernelPackages = pkgs.linuxPackages_xanmod;
networking.firewall.enable = false;
}
sys/default.nix
本文件存放喆我的NixOS的配置。
NixOS的配置通过命令sudo nixos-rebuild switch
生效。
因为NixOS的配置入口是/etc/nixos/configuration.nix
,
所以需要在/etc/nixos/configuration.nix
中import本文件,例如
# /etc/nixos/configuration.nix:
{ config, pkgs, ... }: {
imports = [
./hardware-configuration.nix
# import my sys/ here!
/home/xieby1/.config/nixpkgs/sys
];
# other configs ...
}
NixOS配置可以使用的参数可参考configuration.nix的manpage,即man configuration.nix
。
下面是我的NixOS的配置源码及注解:
# add this file to /etc/nixos/configuration.nix: imports
{ config, pkgs, ... }:
{
让NixOS的nixpkgs使用和home-manager的nixpkgs采用相同的nixpkgs config
nixpkgs.config = import ../config.nix;
导入我的NixOS的CLI和GUI配置, 详细内容见文档./cli和./gui。
imports = [
./modules/cachix.nix
./cli
./gui
];
Nix binary cache的地址。 越靠前优先级越高。 由于cache.nixos.org需要梯子, 这里使用了清华Tuna提供的Nix binary cache镜像来加速。
nix.settings.substituters = [
"https://cache.nixos.org/"
"https://xieby1.cachix.org"
];
设置时区。
time.timeZone = "Asia/Shanghai";
time.hardwareClockInLocalTime = true;
设置Linux账户信息。
users.mutableUsers = false;
当然GitHub上当然不能明文存储密码,这里使用hash过的密码。
可以使用命令行工具mkpasswd
来生成hash过的密码。
给root
用户设置hash过的密码。
users.users.root.hashedPassword = "$6$wRBpbr4zSTA/nh$XI/KUASw3mELIqyAxN1hUTWizz9ZBzPhP2u4HNDCA49h4KOWkZsyuiextyXkUti7jYsUHE9fTiRjGAoxBg0Gq/";
users.users.xieby1 = {
isNormalUser = true;
createHome = true;
同上,给xieby1
用户设置hash过的密码。
hashedPassword = "$6$Y4KJxhdaJTT$RSolbCpaUKK2UW1cdnuH.8n1Ky9p0Lnx0MP36BxGX9Q2AeVMjCp.bZOsZ11w689je/785TFRQoVgicMiOfA9B.";
给用户xieby1
启用sudo。
extraGroups = [ "wheel" ];
ssh授权的公钥。这样设置后,我的所有的NixOS都相当于“自动授权”了。
我的/home/xieby1/Gist/
文件夹存放着一些不方便放入Git仓库的文件,比如二进制文件,或是隐私文件。
该文件由syncthing进行多设备同步。
简单的说,我的备份理念是“git备份配置,syncthing备份数据”。
“配置”即指这个nix_config仓库,“数据”指~/Gist/
、~/Documents/
等文件夹。
有了这些备份就能轻松还原/复现我的整个工作环境。
TODO:单独专门介绍我的备份理念。
openssh.authorizedKeys.keyFiles = [] ++ (
if builtins.pathExists /home/xieby1/Gist/Vault/y50_70.pub
then [/home/xieby1/Gist/Vault/y50_70.pub]
else []
) ++ (
if builtins.pathExists /home/xieby1/Gist/Vault/yoga14s.pub
then [/home/xieby1/Gist/Vault/yoga14s.pub]
else []
);
};
让TTY自动登录我的账户,这样就可以自动启动用户级(user)的systemd服务了。 这样就可以在非NixOS中(比如Ubuntu服务器、WSL2、Debian树莓派等) 自动拉起systemd用户服务(比如syncthing、clash、tailscale等)。
services.getty.autologinUser = "xieby1";
有关systemd用户服务的配置,详细可见参考:
- home-manager配置的manpage的services词条,
比如
man home-configuration.nix
搜索services.syncthing
- 我的syncthing配置./usr/cli: syncthing
- 我的clash配置./usr/cli/clash.nix
- 我的tailscale配置./usr/cli/tailscale.nix
}
{ config, pkgs, ... }:
{
imports = [ # files
] ++ [{ # functions & attrs
# support fractional scaling for x11 gnome:
# refer to https://nixos.wiki/wiki/Overlays#Overriding_a_package_inside_a_scope
nixpkgs.overlays = [ (final: prev: {
mutter = let
mutter-x11-scaling = pkgs.fetchFromGitHub {
owner = "puxplaying";
repo = "mutter-x11-scaling";
rev = "d19eeb27f6efb1f489ef49e0c987a9e02a072b31";
hash = "sha256-NOMd7wyQNj+tp2WZ9l2tKITYUvL7toZ8OvVQssR14Ng=";
};
in prev.mutter.overrideAttrs (old: {
patches = (pkgs.lib.optionals (old ? patches) old.patches) ++ [
"${mutter-x11-scaling}/x11-Add-support-for-fractional-scaling-using-Randr.patch"
];
});
gnome-control-center = let
gnome-control-center-x11-scaling = pkgs.fetchFromGitHub {
owner = "puxplaying";
repo = "gnome-control-center-x11-scaling";
rev = "ec599022bbd39455043eeb5dd03ea6f1d33bbe23";
hash = "sha256-Y5D1p+koFBfgkbgreIV8FE+TqJrA8TJIrRQ8cQkzdB8=";
};
in prev.gnome-control-center.overrideAttrs (old: {
patches = (pkgs.lib.optionals (old ? patches) old.patches) ++ [
"${gnome-control-center-x11-scaling}/display-Support-UI-scaled-logical-monitor-mode.patch"
"${gnome-control-center-x11-scaling}/display-Allow-fractional-scaling-to-be-enabled.patch"
];
});
}) ];
# push the overrided mutter and gnome to my cachix
cachix_packages = with pkgs; [mutter gnome-control-center];
}] ;
# Enable the X11 windowing system.
services.xserver.enable = true;
services.xserver.videoDrivers = [
# "nvidia"
# default video drivers
"radeon" "nouveau" "modesetting" "fbdev"
"modesetting" "amdgpu"
];
# Enable the GNOME Desktop Environment.
services.xserver.displayManager.gdm.enable = true;
services.xserver.displayManager.gdm.wayland = false;
services.xserver.desktopManager.gnome.enable = true;
# https://discourse.nixos.org/t/how-to-create-folder-in-var-lib-with-nix/15647
system.activationScripts.user_account_conf = pkgs.lib.stringAfter [ "var" ] (let
face = pkgs.fetchurl {
url = "https://github.com/xieby1.png";
sha256 = "1s20qy3205ljp29lk0wqs6aw5z67db3c0lvnp0p7v1q2bz97s9bm";
};
in ''
mkdir -p /var/lib/AccountsService/users
if [[ ! -f /var/lib/AccountsService/users/xieby1 ]]; then
cat > /var/lib/AccountsService/users/xieby1 <<XIEBY1_ACCOUNT
[User]
Session=
Icon=/var/lib/AccountsService/icons/xieby1
SystemAccount=false
XIEBY1_ACCOUNT
fi
mkdir -p /var/lib/AccountsService/icons
ln -sf ${face} /var/lib/AccountsService/icons/xieby1
'');
# Configure keymap in X11
# services.xserver.layout = "us";
# services.xserver.xkbOptions = "eurosign:e";
# Enable CUPS to print documents.
# https://nixos.wiki/wiki/Printing
services.printing.enable = true;
services.printing.drivers = [pkgs.hplip];
services.avahi.enable = true;
services.avahi.nssmdns4 = true;
# Enable touchpad support (enabled default in most desktopManager).
services.libinput.enable = true;
# https://github.com/kovidgoyal/kitty/issues/403
environment.variables.GLFW_IM_MODULE = "ibus";
i18n.inputMethod.enable = true;
i18n.inputMethod.type = "ibus";
i18n.inputMethod.ibus.engines = with pkgs.ibus-engines; [
rime
# keyboard layout is wrong in anthy, e.g. punctuations
# anthy
# hinagara toggle setting is absent in mozc
mozc
hangul
];
nixpkgs.config.allowUnfree = true;
# vim vista need nerd fonts
# https://github.com/liuchengxu/vista.vim/issues/74
# https://github.com/liuchengxu/space-vim/wiki/tips#programming-fonts
# available nerd fonts: nixpkgs/pkgs/data/fonts/nerdfonts/shas.nix
## use non-variable noto font for feishu and other old electron apps
## for more details see: https://github.com/NixOS/nixpkgs/issues/171976
fonts.packages = with pkgs; [
noto-fonts-cjk-sans
noto-fonts-cjk-serif
noto-fonts-emoji
noto-fonts-color-emoji
noto-fonts-extra
# The best developer fonts, see https://www.nerdfonts.com/
nerd-fonts.hack
nerd-fonts.meslo-lg
nerd-fonts.sauce-code-pro
nerd-fonts.fira-code
nerd-fonts.terminess-ttf
nerd-fonts.iosevka
nerd-fonts.monoid
nerd-fonts.fantasque-sans-mono
];
# enable fontDir /run/current-system/sw/share/X11/fonts
fonts.fontDir.enable = true;
fonts.fontconfig.defaultFonts = {
monospace = [
"DejaVu Sans Mono"
"Noto Color Emoji"
"Noto Emoji"
];
};
services.gpm.enable = true;
hardware.xone.enable = true;
# for x11 gesture
services.touchegg.enable = true;
}
{ config, lib, ...}:
{
imports = [../../modules/cachix.nix];
config = lib.mkIf (
(builtins.pathExists config.cachix_dhall) &&
(config.cachix_packages != [])
) {
system.activationScripts = {
cachix_push = config._cachix_push;
};
};
}
Currently, Not Wayland, But X11
Here list wayland problems I have met.
warpd not work
fcitx input method not work
waydroid
waydroid tile not work
tabbed not work
gnome terminal headerbar cannot be hidden
Both gnome extension unite and gtk-title-bar cannot hide gnome terminal(kitty and alacritty)'s headerbar.
- https://github.com/velitasali/gtktitlebar/issues/25
- https://github.com/velitasali/gtktitlebar/issues/14
autokey and espanso not work in wayland
Autokey only works in X11, while espanso officially disclaim it support wayland. But my wayland experience is, espanso does not work either.
home.nix
{ config, pkgs, stdenv, lib, ... }:
let
clashctl = pkgs.callPackage ./clashctl.nix {};
in {
imports = [{
home.packages = [
pkgs.clash-meta
] ++ lib.optional (!config.isNixOnDroid) clashctl;
cachix_packages = lib.optional (!config.isNixOnDroid) clashctl;
systemd.user.services.clash = {
Unit = {
Description = "Auto start clash";
After = ["network.target"];
};
Install = {
WantedBy = ["default.target"];
};
Service = {
ExecStart = "${pkgs.clash-meta.outPath}/bin/clash-meta -d ${config.home.homeDirectory}/Gist/clash";
};
};
programs.bash.bashrcExtra = lib.mkBefore (lib.optionalString (!config.isNixOnDroid) ''
# proxy
## default
HTTP_PROXY="http://127.0.0.1:${toString config.proxyPort}/"
## microsoft wsl
# if [[ $(uname -r) == *"microsoft"* ]]; then
# hostip=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
# export HTTP_PROXY="http://$hostip:${toString config.proxyPort}"
# fi
export HTTPS_PROXY="$HTTP_PROXY"
export HTTP_PROXY="$HTTP_PROXY"
export FTP_PROXY="$HTTP_PROXY"
export http_proxy="$HTTP_PROXY"
export https_proxy="$HTTP_PROXY"
export ftp_proxy="$HTTP_PROXY"
'');
}];
}
{ lib
, rustPlatform
, fetchFromGitHub
}:
rustPlatform.buildRustPackage {
name = "clashctl";
src = fetchFromGitHub {
# owner = "https://github.com/George-Miao/clashctl";
owner = "George-Miao";
repo = "clashctl";
rev = "b09e1faf80f1a25fa855499d8b34d36491e5a081";
hash = "sha256-c7y64SsZEKdC8+umCY8+XBwxAHxn4YpqR48ASbHpkdM=";
};
cargoHash = "sha256-Dt8AikjIaNTZ03vT/zV6ZTQ5QNoOjVxYYIc1AB/3De8=";
doCheck = false;
meta = with lib; {
description = "CLI for interacting with clash";
homepage = "https://github.com/George-Miao/clashctl";
license = licenses.mit;
maintainers = with maintainers; [ xieby1 ];
};
}
{ pkgs, ... }: {
home.packages = with pkgs; [
universal-ctags
];
home.file.exclude_ctags = {
text = ''
# exclude ccls generated directories
--exclude=.ccls*
'';
target = ".config/ctags/exclude.ctags";
};
home.file.scala_ctags = {
target = ".config/ctags/scala.ctags";
text = ''
--langdef=Scala
--langmap=Scala:.scala
--regex-Scala=/^[ \t]*class[ \t]*([a-zA-Z0-9_]+)/\1/c,classes/
--regex-Scala=/^[ \t]*object[ \t]*([a-zA-Z0-9_]+)/\1/o,objects/
--regex-Scala=/^[ \t]*trait[ \t]*([a-zA-Z0-9_]+)/\1/t,traits/
--regex-Scala=/^[ \t]*case[ \t]*class[ \t]*([a-zA-Z0-9_]+)/\1/r,cclasses/
--regex-Scala=/^[ \t]*abstract[ \t]*class[ \t]*([a-zA-Z0-9_]+)/\1/a,aclasses/
--regex-Scala=/^[ \t]*def[ \t]*([a-zA-Z0-9_=]+)[ \t]*.*[:=]/\1/m,methods/
--regex-Scala=/[ \t]*val[ \t]*([a-zA-Z0-9_]+)[ \t]*[:=]/\1/V,values/
--regex-Scala=/[ \t]*var[ \t]*([a-zA-Z0-9_]+)[ \t]*[:=]/\1/v,variables/
--regex-Scala=/^[ \t]*type[ \t]*([a-zA-Z0-9_]+)[ \t]*[\[<>=]/\1/T,types/
--regex-Scala=/^[ \t]*import[ \t]*([a-zA-Z0-9_{}., \t=>]+$)/\1/i,includes/
--regex-Scala=/^[ \t]*package[ \t]*([a-zA-Z0-9_.]+$)/\1/p,packages/
'';
};
programs.neovim = {
extraConfig = ''
"" tags support, ';' means upward search, referring to http://vimdoc.sourceforge.net/htmldoc/editing.html#file-searching
set tags=./tags;
'';
plugins = [{
A bug in neovim or lspconfig?
I want to jump to the definition of QEMU's qemu_clock_get_ns
using ctags.
More specifically, when I press g]
with my cursor under a qemu_clock_get_ns
call,
neovim only lists one definition:
# pri kind tag file
1 F Functionqemu_clock_get_ns /home/xieby1/Codes/qemu/tests/unit/ptimer-test-stubs.c
\%86l\%9c
Type number and <Enter> (q or empty cancels):
However, there should be two definitions! After tweaking a lot, I found out that clangd's lspconfig config directly or indirectly causes this.
require('lspconfig').clangd.setup{
filetypes = { "c", "cc", "cpp", "c++", "objc", "objcpp", "cuda", "proto" }
}
As long as the filetype of the current buffer is not clangd-related, ctags works correctly.
For example, if I cd <qemu-source-code>
, then run vim
, and finally :ts qemu_clock_get_ns
,
I get the expected result:
# pri kind tag file
1 F f qemu_clock_get_ns tests/unit/ptimer-test-stubs.c
typeref:typename:int64_t
int64_t qemu_clock_get_ns(QEMUClockType type)
2 F f qemu_clock_get_ns util/qemu-timer.c
typeref:typename:int64_t
int64_t qemu_clock_get_ns(QEMUClockType type)
Type number and <Enter> (q or empty cancels):
I searched through neovim's and nvim-lspconfig's github issues but didn't find any related issues.
I also tried searching for the weird string \%.*l\%.*c
(%86l%9c) in neovim and lspconfig source code,
but found nothing.
As a result, I've decided to use a wrapper plugin for tags functionality.
nvim-telescope has builtin support tags :lua require'telescope.builtin'.tags{}
.
However it is VERY SLOW!
So, I choose to install a plugin: nvim-telescope-ctags-plus.
plugin = pkgs.vimUtils.buildVimPlugin {
name = "nvim-telescope-ctags-plus";
src = pkgs.fetchFromGitHub {
owner = "gnfisher";
repo = "nvim-telescope-ctags-plus";
rev = "455f24c0dcc6126c89cd3a3278e3fe322df061b1";
hash = "sha256-P9uYkWvY4NzwlAxG40/0sNZoGEHezDhlYL/gKfBI/dA=";
};
doCheck = false;
};
type = "lua";
config =''
require('telescope').load_extension('ctags_plus')
-- in vimscript:
-- nnoremap g] <cmd>lua require('telescope').extensions.ctags_plus.jump_to_tag()<cr>
vim.keymap.set('n', 'g]', function()
require('telescope').extensions.ctags_plus.jump_to_tag()
end, { noremap = true, silent = true })
'';
}];
};
}
{ config, pkgs, stdenv, lib, ... }:
let
fzf-doc = pkgs.writeScriptBin "fzf-doc" ''
allCmds() {
# bash alias
compgen -A alias
# external commands
# https://unix.stackexchange.com/questions/94775/list-all-commands-that-a-shell-knows
case "$PATH" in
(*[!:]:) PATH="$PATH:" ;;
esac
set -f
IFS_OLD="$IFS"
IFS=:
for dir in $PATH; do
set +f
[ -z "$dir" ] && dir="."
for file in "$dir"/*; do
if [ -x "$file" ] && ! [ -d "$file" ]; then
echo "''${file##*/}"
fi
done
done
IFS="$IFS_OLD"
}
cd ~/Documents
FILE=$(fzf)
[ -z "$FILE" ] && exit
CMD=$(allCmds | fzf)
[ -z "$CMD" ] && exit
case "$CMD" in
# run gui cmd background
o)
# use nohup to run bash command in background and exit
## https://superuser.com/questions/448445/run-bash-script-in-background-and-exit-terminal
# nohup not recognize bash alias like `o`, it's necessary to call bash
eval nohup bash -ic '"$CMD \"$FILE\" &"'
;;
# run cli cmd foreground
*)
# FILE name may contain space, quote FILE name
eval "$CMD" \"$FILE\"
;;
esac
'';
sysconfig = (
# <...> are expression search in NIX_PATH
if (builtins.tryEval <nixos-config>).success
then import <nixpkgs/nixos> {}
else import <nixpkgs/nixos> {configuration={};}
).config;
in
{
imports = [
./extra.nix
./vim
./tmux.nix
./clash.nix
./tailscale.nix
./gdb.nix
./ctags.nix
./ssh.nix
./git.nix
] ++ [{ # functions & attrs
home.packages = [
pkgs.fzf
fzf-doc
];
programs.bash.bashrcExtra = ''
# FZF top-down display
export FZF_DEFAULT_OPTS="--reverse"
'';
}{
home.packages = lib.optional (!config.isNixOnDroid) pkgs.hstr;
programs.bash.bashrcExtra = lib.optionalString (!config.isNixOnDroid) ''
# HSTR configuration - add this to ~/.bashrc
alias hh=hstr # hh to be alias for hstr
export HSTR_CONFIG=hicolor,raw-history-view
shopt -s histappend # append new history items to .bash_history
export HISTCONTROL=ignorespace # leading space hides commands from history
# ensure synchronization between bash memory and history file
export PROMPT_COMMAND="history -a;"
# if this is interactive shell, then bind hstr to Ctrl-r (for Vi mode check doc)
if [[ $- =~ .*i.* ]]; then bind '"\C-r": "\C-a hstr -- \C-j"'; fi
# if this is interactive shell, then bind 'kill last command' to Ctrl-x k
if [[ $- =~ .*i.* ]]; then bind '"\C-xk": "\C-a hstr -k \C-j"'; fi
'';
}{
home.packages = [pkgs.nix-index];
home.file.nix_index_database = {
source = builtins.fetchurl "https://github.com/Mic92/nix-index-database/releases/latest/download/index-${builtins.currentSystem}";
target = ".cache/nix-index/files";
};
}{
Syncthing
services.syncthing = {
enable = true;
让syncthing的端口外部可访问。
extraOptions = lib.optional config.isCli "--gui-address=0.0.0.0:8384";
};
启用代理,因为有些syncthing的服务器似乎是被墙了的。
systemd.user.services.syncthing.Service.Environment = [
# https://docs.syncthing.net/users/proxying.html
"http_proxy=http://127.0.0.1:${toString config.proxyPort}"
];
使用命令行浏览器browsh来实现syncthing-tui。
home.packages = lib.optional (!config.isMinimalConfig) (
pkgs.writeShellScriptBin "syncthing-tui" ''
${pkgs.browsh}/bin/browsh --firefox.path ${pkgs.firefox}/bin/firefox http://127.0.0.1:8384
''
);
}{
home.packages = with pkgs; [
cachix
];
home.file.cachix_dhall = {
source = if (builtins.pathExists ~/Gist/Config/cachix.dhall)
then ~/Gist/Config/cachix.dhall
else builtins.toFile "empty-cachix.dhall" "";
target = ".config/cachix/cachix.dhall";
};
}];
home.packages = with pkgs; [
# tools
parallel
comma
xclip
python3Packages.qrcode
ripgrep
## archive
unar
## manage
htop
nix-tree
## text
pandoc
## compile
gnumake
makefile2graph
remake
## draw
graphviz
figlet
nyancat
d2
## manual
tldr
## file system
file
# magika # detect file content types with deep learning
tree
## network
wget
axel
bind.dnsutils # nslookup
netcat
nload
nmap
nethogs
nodePackages.browser-sync
## x11
xdotool
# programming
inotify-tools
clang-tools
cmake
capstone
scc
## https://stackoverflow.com/questions/40165650/how-to-list-all-files-tracked-by-git-excluding-submodules
(writeShellScriptBin "scc-git" "${scc}/bin/scc $(git grep --cached -l '')")
sloccount
flamegraph
## python
( python3.withPackages ( p: with p; [
ipython
pydot
]))
## c
(lib.setPrio # make bintools less prior
(bintools-unwrapped.meta.priority + 1)
bintools-unwrapped
)
(if builtins.currentSystem == "x86_64-linux"
then gcc_multi
else gcc
)
### docs
stdmanpages
man-pages
#gccStdenv
bear
## xml
libxml2
## bash
bc
## nix
nixos-option
nix-output-monitor
### allow non-nixos access `man configuration.nix`
# see: nixos/modules/misc/documentation.nix
# nixos/doc/manual/default.nix
sysconfig.system.build.manual.nixos-configuration-reference-manpage
nurl
];
programs.eza.enable = true;
# bash
programs.bash.enable = true;
programs.bash.shellAliases.o = "xdg-open";
programs.bash.bashrcExtra = ''
# rewrite prompt format
u_green="\[\033[01;32m\]"
u_blue="\[\033[01;34m\]"
u_white="\[\033[00m\]"
PS1="''${debian_chroot:+($debian_chroot)}"
if [[ $HOSTNAME =~ qemu.* ]]; then
PS1+="(qemu)"
fi
if [[ -n "$IN_NIX_SHELL" ]]; then
PS1+="(''${name}.$IN_NIX_SHELL)"
fi
PS1+="''${u_green}\u${lib.optionalString (!config.isNixOnDroid) "@\\h"}''${u_white}:"
PS1+="''${u_blue}\w''${u_white}"
PS1+="\n''${u_green}\$''${u_white} "
unset u_green u_blue u_white
## change title
### https://unix.stackexchange.com/questions/177572/
PS1+="\[\e]2;\w\a\]"
# nixos obsidian
export NIXPKGS_ALLOW_INSECURE=1
# source my bashrc
if [[ -f ~/Gist/Config/bashrc ]]; then
source ~/Gist/Config/bashrc
fi
# user nix config setting
export NIX_USER_CONF_FILES=~/.config/nixpkgs/nix/nix.conf
if [[ -e ~/.nix-profile/etc/profile.d/nix.sh ]]; then
source ~/.nix-profile/etc/profile.d/nix.sh
fi
# bash-completion, inspired by
## https://discourse.nixos.org/t/whats-the-nix-way-of-bash-completion-for-packages/20209/16
# system tools completion, e.g. nix
XDG_DATA_DIRS+=":${sysconfig.system.path}/share"
# home tools completion
XDG_DATA_DIRS+=":${config.home.path}/share"
export XDG_DATA_DIRS
. ${pkgs.bash-completion}/etc/profile.d/bash_completion.sh
# 解决tmux在nix-on-droid上不显示emoji和中文的问题
export LANG=C.UTF-8
if [[ -n $(command -v eza) ]]; then
alias ls=eza
fi
'' + lib.optionalString config.isWSL2 ''
# use the working directory of the current tab as the starting directory for a new tab
# https://learn.microsoft.com/en-us/windows/terminal/tutorials/new-tab-same-directory#using-actions-to-duplicate-the-path
PROMPT_COMMAND=''${PROMPT_COMMAND:+"$PROMPT_COMMAND"}'printf "\e]9;9;%s\e\\" "$(wslpath -w "$PWD")"'
'';
programs.direnv.enable = true;
programs.direnv.nix-direnv.enable = true;
}
# When init searxng, it throws `ERROR:searx.engines.wikidata: Fail to initialize`
# I have no idea, so deprecated it.
{ config, pkgs, stdenv, lib, ... }:
let
searxng_yml = builtins.toFile "searxng.yml" ''
# https://docs.searxng.org/admin/settings/settings.html#settings-yml-location
# The initial settings.yml we be load from these locations:
# * the full path specified in the SEARXNG_SETTINGS_PATH environment variable.
# * /etc/searxng/settings.yml
# Default settings see <pkgs.searxng>/lib/python3.11/site-packages/searx/settings.yml
use_default_settings: true
search:
autocomplete: "google"
default_lang: "en"
server:
# Is overwritten by $SEARXNG_SECRET
secret_key: ${if builtins.pathExists ~/Gist/Config/passwordFile
then builtins.readFile ~/Gist/Config/passwordFile
else "miao"}
outgoing:
proxies:
all://:
- http://127.0.0.1:${toString config.proxyPort}
engines:
- name: bilibili
engine: bilibili
shortcut: bil
disabled: false
- name: bing
engine: bing
shortcut: bi
disabled: false
- name: qwant
disabled: true
ui:
results_on_new_tab: true
'';
in {
systemd.user.services.searxng = {
Unit = {
Description = "Auto start searxng";
After = ["network.target"];
};
Install = {
WantedBy = ["default.target"];
};
Service = {
Environment = [
"SEARXNG_SETTINGS_PATH=${searxng_yml}"
];
ExecStart = "${pkgs.searxng}/bin/searx-run";
};
};
}
cli-extra.nix: Extra CLI configs (added to minimal cli config)
{ config, pkgs, stdenv, lib, ... }: {
imports = [{
home.packages = [
pkgs.act
(pkgs.writeShellScriptBin "act-podman" ''
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
CMD=(
"${pkgs.act}/bin/act"
"--bind"
# use podman
"--container-daemon-socket" "unix://$XDG_RUNTIME_DIR/podman/podman.sock"
# use host proxy
"--container-options" "--network=host"
"--env" "HTTPS_PROXY='http://127.0.0.1:${toString config.proxyPort}'"
"--env" "HTTP_PROXY='http://127.0.0.1:${toString config.proxyPort}'"
"--env" "FTP_PROXY='http://127.0.0.1:${toString config.proxyPort}'"
"--env" "https_proxy='http://127.0.0.1:${toString config.proxyPort}'"
"--env" "http_proxy='http://127.0.0.1:${toString config.proxyPort}'"
"--env" "ftp_proxy='http://127.0.0.1:${toString config.proxyPort}'"
"$@"
)
eval "''${CMD[@]}"
'')
];
}];
config = lib.mkIf config.isMinimalConfig {
home.packages = with pkgs; [
# tools
imagemagick
# programming
## c
cling # c/cpp repl
## javascript
nodePackages.typescript
### node
nodejs
## java
openjdk
### pdfcrop
(texlive.combine {inherit (pkgs.texlive) scheme-minimal pdfcrop;})
# runXonY
qemu
] ++ lib.optional (builtins.currentSystem == "x86_64-linux") quickemu;
};
}
GDB configurations
{ pkgs, ... }: {
home.packages = [
pkgs.gdb
];
home.file.gdbinit = {
source = pkgs.fetchFromGitHub {
owner = "cyrus-and";
repo = "gdb-dashboard";
rev = "616ed5100d3588bb70e3b86737ac0609ce0635cc";
hash = "sha256-xoBkAFwkbaAsvgPwGwe1JxE1C8gPR6GP1iXeNKK5Z70=";
} + /.gdbinit;
target = ".gdbinit";
};
home.file.gdb_dashboard_init = {
text = ''
# gdb-dashboard init file
# available layout modules
# stack registers history assembly
# breakpoints expressions memory
# source threads variables
dashboard -layout source
# https://en.wikipedia.org/wiki/ANSI_escape_code
#dashboard -style prompt
## fg bold blue
dashboard -style prompt_not_running "\\[\\e[1;34m\\]$\\[\\e[0m\\]"
## fg bold green
dashboard -style prompt_running "\\[\\e[1;32m\\]$\\[\\e[0m\\]"
'';
target = ".gdbinit.d/init";
};
}
{ pkgs, ... }: let
git-wip = builtins.derivation {
name = "git-wip";
system = builtins.currentSystem;
src = pkgs.fetchurl {
url = "https://raw.githubusercontent.com/bartman/git-wip/1c095e93539261370ae811ebf47b8d3fe9166869/git-wip";
sha256 = "00gq5bwwhjy68ig26a62307pww2i81y3zcx9yqr8fa36fsqaw37h";
};
builder = pkgs.writeShellScript "git-wip-builder" ''
source ${pkgs.stdenv}/setup
mkdir -p $out/bin
dst=$out/bin/git-wip
cp $src $dst
chmod +w $dst
sed -i 's/#!\/bin\/bash/#!\/usr\/bin\/env bash/g' $dst
chmod -w $dst
chmod a+x $dst
'';
};
in {
home.packages = with pkgs; [
gitui
mr
git-wip
git-quick-stats
];
programs.git = {
enable = true;
package = pkgs.gitFull;
userEmail = "xieby1@outlook.com";
userName = "xieby1";
extraConfig = {
core = {
editor = "vim";
};
credential.helper = "store";
};
aliases = {
viz = "log --all --decorate --oneline --graph";
};
lfs.enable = true;
};
systemd.user.tmpfiles.rules = [
"L? %h/.mrconfig - - - - %h/Gist/Config/mrconfig"
];
# mr status not work in non-home dir
programs.bash.shellAliases.mr = "mr -d ~";
}
{ buildPythonPackage
, lib
, nss
, nspr
, expat
, fetchPypi
}:
let
rpath = lib.makeLibraryPath [
nss
nspr
expat
];
in buildPythonPackage rec {
pname = "kaleido";
version = "0.2.1";
format = "wheel";
src = fetchPypi {
inherit pname version format;
platform = "manylinux1_x86_64";
hash = "sha256-qiHPG/HHj4+lCp99ReEAPDh709b+CnZ8+780S5W9w6g=";
};
doCheck = false;
postFixup = ''
for file in $(find $out -type f \( -perm /0111 -o -name \*.so\* \) ); do
patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$file" || true
patchelf --set-rpath ${rpath}:$out/lib/x86_64-linux-gnu $file || true
done
sed -i 's,#!/bin/bash,#!/usr/bin/env bash,' $out/lib/python3.11/site-packages/kaleido/executable/kaleido
'';
}
binutils's ld and gcc's ld collsion
Background: I want to install gcc
and objdump
, where objdump
is contained in binutils
.
Home-manager tolds me gcc
's ld
collides with binutils
's ld
.
As I explore into gcc
and binutils
, weird thing comes.
Packages binutils
and gcc
both contain ld executable.
binutils
and gcc
has the same priority 10.
The WEIRD thing is,
binutils
wants to have a lower priority than gcc-wrapper, so sets its priority to 10, see nixpkgs:pkgs/development/tools/misc/binutils/default.nix
Give binutils a lower priority than gcc-wrapper to prevent a collision due to the ld/as wrappers/symlinks in the latter.
- Both
gcc
wrapper and all-packages set gcc priority to 10, see nixpkgs:pkgs/top-level/all-packages.nix
andpkgs/build-support/cc-wrapper/default.nix
All-packages set priority by lowPrio function, which will set priority to 10. Cc-wrapper directly set priority to 10.
As a result, binutils
will definitely collide with gcc
!
My solution: assign binutils
a lower priority, like this
home.packages = with pkgs; [
(lib.setPrio # higher value, less prior
(bintools-unwrapped.meta.priority + 1)
bintools-unwrapped
)
gdb
]
Nodejs packages (npm) in NixOS
NixOS中的Nodejs包(npm)
太长不看:手动暴露buildNodePackage
,添加自定义的npm包。
nixpkgs中的node包虽多但有限, 遇到需要的node包不存在时,是个麻烦事。
让我觉得十分诡异的是,
nixpkgs中存在一个十分方便的添加node包的函数nodeEnv.buildNodePackage
,
但是这个函数却不暴露出来给用户。
甚至,有些nixpkgs中的包为了使用这个函数,
复制粘贴该函数到自己的包中。
在这些奇怪的事情的基础上,
还衍生出来自动生成该buildNodePackage
函数软件node2nix
。
所以这真的是存在即合理嘛? 还是我学艺不精,不能理解设计者的意图?
一个简单的暴露buildNodePackage
的方法,
直接导入buildNodePackage
所在的文件。
为了跨平台可用,
路径的构建采用了一点使用了点点小把戏,
<nixpkgs> + /pkgs/development/node-packages/node-env.nix
以@types/node为例子,nodepkgs.nix:
{ ... }:
let
pkgs = import <nixpkgs> {};
nodeEnv = import (<nixpkgs> + /pkgs/development/node-packages/node-env.nix) {
inherit (pkgs) lib stdenv nodejs python2;
inherit pkgs;
inherit (pkgs) libtool runCommand writeTextFile writeShellScript;
};
globalBuildInputs = [];
in {
"@types/node" = nodeEnv.buildNodePackage {
name = "_at_types_slash_node";
packageName = "@types/node";
version = "18.7.15";
src = pkgs.fetchurl {
url = "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz";
sha512 = "XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==";
};
buildInputs = globalBuildInputs;
meta = {
description = "TypeScript definitions for Node.js";
homepage = "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node";
license = "MIT";
};
production = true;
bypassCache = true;
reconstructLock = true;
};
}
该nodepkgs.nix文件为用户自定义的node包, 可以像nixpkgs一样使用这个文件,例如导入
myNodePkgs = import ./cli/nodepkgs.nix {}
包的表达式为myNodePkgs."@types/node"
。
Python in Nix/NixOS
Project | Maintained | Description | Purity | Compatibility |
---|---|---|---|---|
venv | ❌ | 100%? | ||
mach-nix | ✅ | |||
pypi2nix | ❌ | |||
pip2nix | ✅ | ✅ | ||
buildPythonPackage | ✅ | ✅ | ||
[TODO] poetry | ||||
[TODO] dream2nix |
pip2nix
Noted: Latest version of pip2nix only use python39. While nixpkgs 22.11 (current stable) is python310. I dont to install multiple versions of python3.
If pip2nix can overcome the disadvantage of that generate a long list of python packages, and reuse python3Packages in nixpkgs, I would prefer to use pip2nix completely.
Currently, I use buildPythonPackage
.
buildPythonPackage
pkgs.python3Packages.buildPythonPackage
venv
Use virtualenv, pip
is available, no need to write nix expressions to install packages.
Code see https://xieby1.github.io/scripts/index.html#venvnix
poetry
According to What is the correct way to setup pip with nix to install any python package in an isolated nix environment, I found two useful tools to install python packages in Nix/NixOS
- [TODO] poetry
- mach-nix
mach-nix
mach-nix github repo: github.com/DavHau/mach-nix
Here is [python_mach.nix]({{ site.repo_url }}/scripts/shell/python_mach.nix), an example which create a shell with a python package called expmcc.
{ config, lib, pkgs, ... }: {
home.packages = [
pkgs.sshfs
];
programs.ssh.enable = true;
programs.ssh.includes = lib.optional (builtins.pathExists ~/Gist/Config/ssh.conf) "~/Gist/Config/ssh.conf";
programs.bash.bashrcExtra = lib.optionalString config.isNixOnDroid ''
# start sshd
if [[ -z "$(pidof sshd-start)" ]]; then
tmux new -d -s sshd-start sshd-start
fi
'';
}
静态链接,以qemu为例
太长不看
- 实现了qemu静态链接,无任何动态链接库的依赖。 采用了两个版本的qemu,当前版本Nix的qemu(nix 21.11, qemu 6.1.1) 和qemu3.1.0这两个版本。
- nix脚本:pkgs_qemu_static.nix
pkgsStatic及其源代码
Nixpkgs含有pkgsStatic软件包,但是大多软件无法直接使用。 需要进行手动修复。
pkgsStatic加载过程的源码
-
pkgs/top-level/stage.nix 将crossSystem设置为static且abi为musl。 之所以用Musl是因为,
Currently uses Musl on Linux (couldn’t get static glibc to work).
其代码大致的如下,
pkgsStatic = nixpkgsFun { crossSystem = { isStatic = true; parsed = stdenv.hostPlatform.parsed // { abi = musl; };};};
- nixpkgsFun = newArgs: import pkgs/top-level/default.nix (...)
- pkgs/top-level/default.nix: stdenvStages {..., crossSystem, ...}
- pkgs/stdenv/default.nix: stagesCross
- pkgs/stdenv/cross/default.nix: stdenvAdapters.makeStatic
- pkgs/stdenv/adapters.nix: makeStatic
- makeStaticLibraries
- 添加configureFlags "--enable-static" "--disable-shared"
- 添加cmakeFlags "-DBUILD_SHARED_LIBS:BOOL=OFF"
- 添加mesonFlags "-Ddefault_library=static"
- makeStaticBinaries
- 添加NIX_CFLAGS_LINK "-static"
- 添加configureFlags "--disable-shared"
- makeStaticLibraries
- pkgs/stdenv/adapters.nix: makeStatic
- pkgs/stdenv/cross/default.nix: stdenvAdapters.makeStatic
- pkgs/stdenv/default.nix: stagesCross
- pkgs/top-level/default.nix: stdenvStages {..., crossSystem, ...}
- nixpkgsFun = newArgs: import pkgs/top-level/default.nix (...)
TODO: 读懂makeStaticLibraries和makeStaticBinaries。
基本思路
对于当前版本的qemu,使用pkgsStatic.qemu ,挨个修复编译报错即可。
对于历史版本的qemu,以3.1.0为例,编译方式已和现在有区别, 比如之前完全是configure, make,而现在引入了meson。 初步设想两种方案,
- 嫁接老版本nixpkgs到当前版本 将qemu-3.1.0对应的nixpkgs嫁接到当前的nixpkgs
- 改造新版本nixpkgs以适应老版本 通过overlay,override方法修改当前nixpkgs的qemu的nix表达式
我采用的第一种办法,将老版本nixpkgs嫁接到当前版本nixpkgs。 使用lazamar.co.uk/nix-versions 查询支持qemu-3.1.0的nixpkgs版本。 引入qemu-3.1.0的示例代码如下,详细见pkgs_qemu_static.nix
# get old nixpkgs which contains qemu-3.1.0
oldNixpkgsSrc = builtins.fetchTarball {...}
qemu31 = pkgs.callPackage (
oldNixpkgsSrc + "/pkgs/applications/virtualization/qemu/default.nix"
) {...}
然后挨个修复编译报错即可。
调试手段
还原构建环境
# 保留构建失败的现场
nix-build pkgs_qemu_static.nix -K
进入现场包含env-vars文件和失败时的源码文件夹。 恢复环境,
. ./env-vars
. $stdenv/setup
查看依赖树
nix-store --query --tree <xxx.drv>
结果
目前仅需要命令行的qemu,因此为了省事, 我去掉了声音和图像支持。
注:在NixOS 21.11上和在ubuntu 22配合nix上编译出来的qemu二进制文件一模一样。
xieby1@yoga14s:~ $ pkgs_qemu_static.nix /nix/store/gs6plgyc0jr9i5qams0ifksijnq9hkq2-qemu-static-x86_64-unknown-linux-musl-6.1.1 /nix/store/lm9kl1nm7bs4hy6l4qng03k4srx1x28n-qemu-3.1.0-x86_64-unknown-linux-musl xieby1@yoga14s:~ $ ldd result/bin/qemu-system-x86_64 not a dynamic executable xieby1@yoga14s:~ $ result/bin/qemu-system-x86_64 --version QEMU emulator version 6.1.1 Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers xieby1@yoga14s:~ $ ldd result-2/bin/qemu-system-x86_64 not a dynamic executable xieby1@yoga14s:~ $ result-2/bin/qemu-system-x86_64 --version QEMU emulator version 3.1.0 Copyright (c) 2003-2018 Fabrice Bellard and the QEMU Project developers
Tailscale/Headscale
overview
- client
- tailscale
- tailscaled
- server
- headscale
Command
TODO: exit node
- https://www.reddit.com/r/Tailscale/comments/w9mtow/question_about_headscale_and_routing/
- https://icloudnative.io/posts/how-to-set-up-or-migrate-headscale/
TODO: headtail config TODO: nix config
- rootless service
- root service
tailscale --socket /tmp/tailscaled.sock up --login-server http://82.157.197.100
tailscale --socket /tmp/tailscaled.sock up --login-server http://82.157.197.100 --force-reauth
enable routes, exit node
sudo tailscale --socket /tmp/tailscaled.sock up --login-server http://82.157.197.100 --advertise-routes=0.0.0.0/0,::/0 --accept-routes=true
sudo docker exec headscale headscale nodes routes enable -i 13 -a -r 0.0.0.0/0 -r ::/0
{ config, pkgs, stdenv, lib, ... }:
let
tailscale-bash-completion = builtins.derivation {
name = "tailscale-bash-completion";
system = builtins.currentSystem;
src = builtins.fetchurl "https://gist.githubusercontent.com/cmtsij/f0d0be209224a7bdd67592695e1427de/raw/tailscale";
builder = pkgs.writeShellScript "tailscale-bash-completion-builder" ''
source ${pkgs.stdenv}/setup
dstdir=$out/share/bash-completion/completions
dst=$dstdir/tailscale
mkdir -p $dstdir
cp $src $dst
'';
};
tailscale-wrapper = {suffix, httpPort, socks5Port}: let
tailscale-wrapped = pkgs.writeShellScriptBin "tailscale-${suffix}" ''
tailscale --socket /tmp/tailscale-${suffix}.sock $@
'';
stateDir = "${config.home.homeDirectory}/.local/share/tailscale-${suffix}";
tailscaled-wrapped = pkgs.writeShellScriptBin "tailscaled-${suffix}" ''
TS_LOGS_DIR="${stateDir}" \
${pkgs.tailscale}/bin/tailscaled \
--tun userspace-networking \
--outbound-http-proxy-listen=localhost:${httpPort} \
--socks5-server=localhost:${socks5Port} \
--socket=/tmp/tailscale-${suffix}.sock \
--state=${stateDir}/tailscaled.state \
--statedir=${stateDir} \
$@
'';
tailscale-wrapped-bash-completion = builtins.derivation {
name = "tailscale-${suffix}-bash-completion";
system = builtins.currentSystem;
builder = pkgs.writeShellScript "tailscale-${suffix}-bash-completion-builder" ''
source ${pkgs.stdenv}/setup
reldir=share/bash-completion/completions
dstdir=$out/$reldir
dst=$dstdir/tailscale-${suffix}
mkdir -p $dstdir
touch $dst
echo ". ${tailscale-bash-completion}/$reldir/tailscale" >> $dst
echo "complete -F _tailscale tailscale-${suffix}" >> $dst
'';
};
in {
home.packages = [
tailscale-wrapped
tailscaled-wrapped
tailscale-wrapped-bash-completion
];
systemd.user.services."tailscaled-${suffix}" = {
Unit = {
Description = "Auto start tailscaled-${suffix} userspace network";
After = ["clash.service"];
};
Install = {
WantedBy = ["default.target"];
};
Service = {
Environment = [
"HTTPS_PROXY=http://127.0.0.1:${toString config.proxyPort}"
"HTTP_PROXY=http://127.0.0.1:${toString config.proxyPort}"
"https_proxy=http://127.0.0.1:${toString config.proxyPort}"
"http_proxy=http://127.0.0.1:${toString config.proxyPort}"
];
ExecStart = "${tailscaled-wrapped}/bin/tailscaled-${suffix}";
};
};
programs.bash.bashrcExtra = lib.optionalString config.isNixOnDroid ''
# start tailscale-${suffix}
if [[ -z "$(pidof tailscaled-${suffix})" ]]; then
tmux new -d -s tailscaled-${suffix} tailscaled-${suffix}
fi
'';
};
in {
imports = [{
home.packages = [pkgs.tailscale tailscale-bash-completion];
}
# (tailscale-wrapper {suffix="headscale"; httpPort="1055"; socks5Port="1065";})
(tailscale-wrapper {suffix="official"; httpPort="1056"; socks5Port="1066";})
];
}
tmux
{ config, pkgs, stdenv, lib, ... }: {
home.packages = [pkgs.tmux];
bash config for tmux
Auto start tmux in non-GUI device. mkAfter ensure the tmux config is appended to the tail of .bashrc.
programs.bash.bashrcExtra = lib.mkAfter (lib.optionalString (!config.isGui) ''
# Auto start tmux
# see: https://unix.stackexchange.com/questions/43601/how-can-i-set-my-default-shell-to-start-up-tmux
# ~~1. tmux exists on the system~~, nix ensure that tmux does exist
# 2. we're in an interactive shell, and
# 3. tmux doesn't try to run within itself
if [ -n "$PS1" ] && [[ ! "$TERM" =~ screen ]] && [[ ! "$TERM" =~ tmux ]] && [ -z "$TMUX" ]; then
exec tmux
fi
'');
tmux config file
home.file.tmux = {
text = ''
# display status at top
set -g status-position top
set -g status-right ""
# status bar
## display title on terminal
set -g set-titles on
set -g window-status-format "#F#I #W #{=/-20/…:pane_title}"
set -g window-status-current-format "🐶#F#I #W #{=/-20/…:pane_title}"
## hide status bar when only one window
### refer to
### https://www.reddit.com/r/tmux/comments/6lwb07/is_it_possible_to_hide_the_status_bar_in_only_a/
### It not good, since its global!
# if -F "#{==:#{session_windows},1}" "set -g status off" "set -g status on"
# set-hook -g window-linked 'if -F "#{==:#{session_windows},1}" "set -g status off" "set -g status on"'
# set-hook -g window-unlinked 'if -F "#{==:#{session_windows},1}" "set -g status off" "set -g status on"'
## color
### colour256的前10个和终端(gnome-terminal tango)的配色一致
set -g status-style "bg=white fg=black"
set -g window-status-last-style "bg=white fg=green bold"
set -g window-status-current-style "bg=black fg=green bold"
# set -g window-status-separator "|"
# enable mouse scroll
set -g mouse on
# window index start from 1
set -g base-index 1
setw -g pane-base-index 1
# auto re-number
set -g renumber-windows on
# Set new panes to open in current directory
bind c new-window -c "#{pane_current_path}"
bind '"' split-window -c "#{pane_current_path}"
bind % split-window -h -c "#{pane_current_path}"
# alt-num select window
bind-key -n M-1 select-window -t 1
bind-key -n M-2 select-window -t 2
bind-key -n M-3 select-window -t 3
bind-key -n M-4 select-window -t 4
bind-key -n M-5 select-window -t 5
bind-key -n M-6 select-window -t 6
bind-key -n M-7 select-window -t 7
bind-key -n M-8 select-window -t 8
bind-key -n M-9 select-window -t 9
# ctrl-t new window
bind-key -n C-t new-window -c "#{pane_current_path}"
# vi key bindings
set -g mode-keys vi
set -g status-keys vi
# Home, End key not work in nix-on-droid
# https://stackoverflow.com/questions/18600188/home-end-keys-do-not-work-in-tmux
bind-key -n Home send Escape "OH"
bind-key -n End send Escape "OF"
set -g allow-passthrough on
'';
target = ".tmux.conf";
};
}
📑neovim
xieby1's neovim config!
See default.nix
key bindings of closing windows
{ ... }: {
programs.neovim = {
extraConfig = ''
function CloseWindowsAbove()
let l:curwin = winnr()
while winnr('1k') != l:curwin
wincmd k
close
let l:curwin -= 1
endwhile
endfunction
function CloseWindowsBelow()
let l:curwin = winnr()
wincmd j
while winnr() != l:curwin
close
endwhile
endfunction
command CloseWindowsAbove call CloseWindowsAbove()
command CloseWindowsBelow call CloseWindowsBelow()
nnoremap Z<Up> :call CloseWindowsAbove()<CR>
nnoremap Z<S-Up> :call CloseWindowsAbove()<CR>
nnoremap Z<Down> :call CloseWindowsBelow()<CR>
nnoremap Z<S-Down> :call CloseWindowsBelow()<CR>
" quit all
nnoremap ZA :qa!<CR>
'';
};
}
Code Companion: AI
{ lib, pkgs, ... }: {
Q: Why mkAfter
here?
A: As codecompanion will change the nvim-cmp config,
therefore we need to make codecompanion is setup after nvim-cmp.
Use mkAfter
, make config of codecompanion is placed after all
other normal configs in ~/.config/nvim/init.lua
.
programs.neovim.plugins = lib.mkAfter [{
plugin = pkgs.vimUtils.buildVimPlugin {
pname = "codecompanion.nvim";
# currently lastest
version = "2025-1-27";
src = pkgs.fetchFromGitHub {
owner = "olimorris";
repo = "codecompanion.nvim";
rev = "f8b9dce0468708c3b280abb045927c6864a17869";
hash = "sha256-1EMzh3TaQxj+eMLjizHwntl/6ZqTHRlxFF6IOSROnuA=";
};
# vertical split current window (including multiple buffers), instead of vsplit current buffer
postPatch = ''
substituteInPlace lua/codecompanion/strategies/chat/ui.lua \
--replace-fail "vsplit" "vertical topleft new"
''
# fix the chat window
+ ''
sed -i '/ui.set_win_options/i vim.cmd("set winfixwidth")' lua/codecompanion/strategies/chat/ui.lua
''
Use telescope as selection provider. Though it can be configured like, each slash_commands needs to be configured, which is very cumbersome!
strategies = {
chat = {
slash_commands = {
["file"] = {
callback = "strategies.chat.slash_commands.file",
description = "Select a file using Telescope",
opts = {
provider = "telescope",
contains_code = true,
},
},
["buffer"] = {...}
},
},
},
So I decided to replace all the provider in source code like below:
+ ''
sed -i 's/provider = .*telescope.*/provider = "telescope",/' lua/codecompanion/config.lua
'';
meta.homepage = "https://github.com/olimorris/codecompanion.nvim/";
doCheck = false;
};
type = "lua";
# https://codecompanion.olimorris.dev/configuration/adapters.html
config = ''
require("codecompanion").setup({
adapters = {
deepseek = function()
return require("codecompanion.adapters").extend("deepseek", {
env = {
api_key = "cmd:cat ~/Gist/Vault/deepseek_api_key_nvim.txt",
},
schema = {
model = {
default = "deepseek-chat",
},
},
})
end,
},
display = {
chat = {
window = {
width = 40,
},
diff = {
enabled = true,
},
},
},
strategies = {
chat = {
adapter = "deepseek",
slash_commands = {
["file"] = {
callback = "strategies.chat.slash_commands.file",
description = "Select a file using Telescope",
opts = {
provider = "telescope",
contains_code = true,
},
},
},
},
inline = {
adapter = "deepseek",
},
},
})
-- key bindings of AI
vim.keymap.set('n', '<leader>a', ':CodeCompanionChat Toggle<CR>')
vim.keymap.set('v', '<leader>a', ':CodeCompanionChat Add<CR>')
'';
}];
}
🎨My nvim color scheme
{ config, pkgs, stdenv, lib, ... }:
let
my-color-scheme = {
plugin = pkgs.vimPlugins.sonokai;
config = ''
nnoremap <leader>c :set termguicolors!<CR>
set termguicolors
let g:sonokai_transparent_background = 1
let g:sonokai_colors_override = {
\ 'black': ['#111215', '237'],
\ 'bg0': ['#22232a', '235'],
\ 'bg1': ['#33353f', '236'],
\ 'bg2': ['#444754', '236'],
\ 'bg3': ['#555869', '237'],
\ 'bg4': ['#666a7e', '237'],
\ 'grey': ['#a5a5a6', '246'],
\ 'grey_dim': ['#787879', '240'],
\}
" custom sonokai,
" see section "How to use custom colors" of `:h sonokai.vim`
function! s:sonokai_custom() abort
let l:palette = sonokai#get_palette('default', {})
call sonokai#highlight('StatusLine', l:palette.black, l:palette.fg, 'bold')
call sonokai#highlight('StatusLineNC', l:palette.black, l:palette.grey, 'bold')
endfunction
augroup SonokaiCustom
autocmd!
autocmd ColorScheme sonokai call s:sonokai_custom()
augroup END
colorscheme sonokai
'';
};
in {
programs.neovim = {
plugins = [
my-color-scheme
];
};
}
conform-nvim: formatter
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimPlugins.conform-nvim;
type = "lua";
config = ''
require("conform").setup({
formatters_by_ft = {
nix = { "nixfmt" },
c = { "clang_format" },
cpp = { "clang_format" },
json = { "js_beautify" },
javascript = { "js_beautify" },
},
})
-- refer to https://github.com/stevearc/conform.nvim/blob/master/doc/recipes.md#format-command
vim.api.nvim_create_user_command("TrimWhitespace", function(args)
local range = nil
if args.count ~= -1 then
local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1]
range = {
start = { args.line1, 0 },
["end"] = { args.line2, end_line:len() },
}
end
require("conform").format({ formatters = {"trim_whitespace"}, range = range })
end, { range = true })
vim.keymap.set({'n','v'}, '<leader>f', ':TrimWhitespace<CR>')
vim.api.nvim_create_user_command("Format", function(args)
local range = nil
if args.count ~= -1 then
local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1]
range = {
start = { args.line1, 0 },
["end"] = { args.line2, end_line:len() },
}
end
require("conform").format({ async = true, lsp_fallback = true, range = range })
end, { range = true })
vim.keymap.set({'n','v'}, '<leader>F', ':Format<CR>')
'';
}];
extraPackages = with pkgs; [
nixfmt-rfc-style
clang-tools
nodePackages.js-beautify
];
};
}
{ config, pkgs, stdenv, lib, ... }: {
Plugins with customizations:
imports = [
./fold.nix
./nvim-metals
./conform-nvim.nix
./nvim-lspconfig
./nvim-cmp.nix
./vim-mark.nix
./nvim-treesitter.nix
./nvim-config-local.nix
./leap-nvim.nix
./telescope-nvim.nix
./git-wip.nix
./vim-floaterm.nix
./outline-nvim.nix
./vim-hexokinase.nix
./gitsigns-nvim.nix
./color-scheme.nix
./hbac-nvim.nix
./winshift-nvim.nix
./smartyank-nvim.nix
./mini-nvim.nix
./vim-easy-align.nix
./codecompanion-nvim.nix
./close-windows.nix
./nvim-window-picker.nix
./vim-matchup.nix
./nvim-nav.nix
./markdown-preview-nvim.nix
./venn-nvim.nix
];
programs.bash.shellAliases.view = "nvim -R";
Set nvim as manpager.
see nvim :h :Man
.
nvim manpage huge mange is SLOW! E.g. man configuration.nix.
programs.bash.shellAliases.nman = "env MANPAGER='nvim +Man!' man";
programs.neovim = {
enable = true;
viAlias = true;
vimAlias = true;
vimdiffAlias = true;
Plugins without customizations:
plugins = with pkgs.vimPlugins; [
vim-smoothie
vim-fugitive
vim-nix
vim-markdown-toc
vim-commentary
vim-plugin-AnsiEsc
vim-surround
];
Vim config
extraConfig = ''
" vim
"" Highlight searches
set hlsearch
nnoremap <F3> :nohlsearch<CR>
"" Show line number
set number
"" Always show the signcolumn, otherwise it would shift the text each time
"" diagnostics appear/become resolved
set signcolumn=number
"" indent
"set smartindent " not good, indentation in empty line cannot be auto removed
""" show existing tab with 2 spaces width
set tabstop=2
""" when indenting with '>', use 2 spaces width
set shiftwidth=2
""" On pressing tab, insert spaces
set expandtab
""" no markdown recommended indent style (recommended shiftwidth=4)
let g:markdown_recommended_style = 0
""" line wrap with ident
set breakindent
""" mouse support " select by pressing shift key!
set mouse=a
"" Preview
nnoremap <leader>[ :pc<CR>
"" highlight unwanted whitespace
set list
set listchars=tab:>-,trail:-
"" syntax
syntax on
"" backspace
set backspace=indent,eol,start
"" wrap line
""" https://stackoverflow.com/questions/248102/is-there-any-command-to-toggle-enable-auto-text-wrapping
:function ToggleWrap()
: if (&wrap == 1)
: set nowrap
: else
: set wrap
: endif
:endfunction
nnoremap <F9> :call ToggleWrap()<CR>
set updatetime=400
"" highlight current line
set cursorlineopt=number
augroup CursorLine
au!
au VimEnter,WinEnter,BufWinEnter * setlocal cursorline
au WinLeave * setlocal nocursorline
augroup END
" filetype
augroup filetype
" detect LLVM IR file
au! BufRead,BufNewFile *.ll set filetype=llvm
" cpp " from gem5
au! BufRead,BufNewFile *.hh.inc,*.cc.inc set filetype=cpp
" d2
au! BufRead,BufNewFile *.d2 set filetype=d2
au! FileType d2 setlocal commentstring=#\ %s
augroup END
" set terminal title
"" https://stackoverflow.com/questions/15123477/tmux-tabs-with-name-of-file-open-in-vim
autocmd BufEnter * let &titlestring = "" . expand("%:t")
set title
nnoremap <F10> :echo "hi<" . synIDattr(synID(line("."),col("."),1),"name") . '> trans<'
\ . synIDattr(synID(line("."),col("."),0),"name") . "> lo<"
\ . synIDattr(synIDtrans(synID(line("."),col("."),1)),"name") . ">"<CR>
" highlight
augroup HiglightTODO
autocmd!
autocmd WinEnter,VimEnter * :silent! call matchadd('Todo', 'TODO', -1)
augroup END
" wildmenu
" see: https://github.com/neovim/neovim/pull/11001
cnoremap <expr> <Up> pumvisible() ? "\<Left>" : "\<Up>"
cnoremap <expr> <Down> pumvisible() ? "\<Right>" : "\<Down>"
cnoremap <expr> <Left> pumvisible() ? "\<Up>" : "\<Left>"
cnoremap <expr> <Right> pumvisible() ? "\<Down>" : "\<Right>"
" prevent resizing other windows when splitting/closing a window
set noequalalways
'';
};
}
DrawIt: ascii art drawing
# Currently, DrawIt runs very slowly, and it has not been maintained for a long time, so deprecate it.
{ config, pkgs, stdenv, lib, ... }:
let
DrawIt = pkgs.vimUtils.buildVimPlugin {
name = "DrawIt";
src = pkgs.fetchFromGitHub {
owner = "vim-scripts";
repo = "DrawIt";
rev = "master"; # I believe it wont update ^_*, so its safe
sha256 = "0yn985pj8dn0bzalwfc8ssx62m01307ic1ypymil311m4gzlfy60";
};
# disable <Leader>f binding
postPatch = ''
sed -i '/<Leader>f/d' autoload/DrawIt.vim
'';
};
in {
programs.neovim = {
plugins = [
DrawIt
];
};
}
# 问题
# * svg会变成png,非常不清晰,也没人提问题
# * (幻灯片里)图片多了非常卡,会显示不出来
# * 排版有问题,总有错位,issues说解决了,我这里用的最新版,仍然部分有问题
#
# 结论,不如用MarkdownPreview
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimUtils.buildVimPlugin {
name = "image-nvim";
src = pkgs.fetchFromGitHub {
owner = "3rd";
repo = "image.nvim";
rev = "4c51d6202628b3b51e368152c053c3fb5c5f76f2";
hash = "sha256-r3s19L0NcUfNTR1SQ98z8fEvhTxGRr4/jlicmbzmQZs=";
};
};
type = "lua";
config = ''
require("image").setup({
integrations = {
markdown = {
enabled = true,
only_render_image_at_cursor = true,
},
html = {
enabled = true,
only_render_image_at_cursor = true,
-- [Render HTML images in markdown files #234](https://github.com/3rd/image.nvim/issues/234)
-- need html treesitter
filetypes = { "html", "xhtml", "htm", "markdown" },
},
css = {
enabled = true,
only_render_image_at_cursor = true,
},
},
})
-- add :ImageToggle
-- inspired by [How to toggle markdown image previews with a command? #181](https://github.com/3rd/image.nvim/issues/181)
vim.api.nvim_create_user_command('ImageToggle', function()
local image = require("image")
if image.is_enabled() then
image.disable()
else
image.enable()
end
end, {})
'';
}];
extraPackages = [ pkgs.imagemagick ];
};
}
fold
{ ... }: {
programs.neovim = {
extraConfig = ''
"" Fold
""" fold text to be the first and last line
""" refer to sbernheim4's reply at
""" https://github.com/nvim-treesitter/nvim-treesitter/pull/390
function! GetSpaces(foldLevel)
if &expandtab == 1
" Indenting with spaces
let str = repeat(" ", a:foldLevel / (&shiftwidth + 1) - 1)
return str
elseif &expandtab == 0
" Indenting with tabs
return repeat(" ", indent(v:foldstart) - (indent(v:foldstart) / &shiftwidth))
endif
endfunction
function! MyFoldText()
let startLineText = getline(v:foldstart)
let endLineText = trim(getline(v:foldend))
let indentation = GetSpaces(foldlevel("."))
let spaces = repeat(" ", 200)
let str = indentation . startLineText . " …… " . endLineText . spaces
return str
endfunction
"" toggle foldmethod between manual and indent
set foldmethod=indent
:function ToggleFoldmethod()
: if (&foldmethod == "manual")
: set foldmethod=indent
: else
: set foldmethod=manual
: endif
:endfunction
nnoremap <Leader>z :call ToggleFoldmethod()<CR>:echo &foldmethod<CR>
"" Custom display for text when folding
set foldtext=MyFoldText()
""" Set the foldlevel to a high setting,
""" files are always loaded with opened folds.
set foldlevel=20
'';
};
}
git-wip: auto wip branch
{ config, pkgs, stdenv, lib, ... }:
let
git-wip = pkgs.vimUtils.buildVimPlugin {
name = "git-wip";
src = pkgs.fetchFromGitHub {
owner = "bartman";
repo = "git-wip";
rev = "1c095e93539261370ae811ebf47b8d3fe9166869";
hash = "sha256-rjvg6sTOuUM3ltD3DuJqgBEDImLrsfdnK52qxCbu8vo=";
};
preInstall = "cd vim";
};
in {
programs.neovim = {
plugins = [
git-wip
];
};
}
gitsigns-nvim: git support
{ config, pkgs, stdenv, lib, ... }:
let
my-gitsigns-nvim = {
plugin = pkgs.vimPlugins.gitsigns-nvim;
type = "lua";
config = ''
require('gitsigns').setup {
signs_staged_enable = false,
signcolumn = false,
numhl = true,
linehl = true,
current_line_blame = true,
current_line_blame_formatter = '🖂 <author> 🖃 <author_time:%R> 🖆 <summary>',
-- keymaps
on_attach = function(bufnr)
local gs = package.loaded.gitsigns
local function map(mode, l, r, opts)
opts = opts or {}
opts.buffer = bufnr
vim.keymap.set(mode, l, r, opts)
end
-- Navigation
map('n', ']c', function()
if vim.wo.diff then return ']c' end
vim.schedule(function() gs.next_hunk() end)
return '<Ignore>'
end, {expr=true})
map('n', '[c', function()
if vim.wo.diff then return '[c' end
vim.schedule(function() gs.prev_hunk() end)
return '<Ignore>'
end, {expr=true})
-- Actions
map({'n', 'v'}, '<leader>hs', ':Gitsigns stage_hunk<CR>')
map({'n', 'v'}, '<leader>hr', ':Gitsigns reset_hunk<CR>')
map('n', '<leader>hS', gs.stage_buffer)
map('n', '<leader>hu', gs.undo_stage_hunk)
map('n', '<leader>hR', gs.reset_buffer)
map('n', '<leader>hp', gs.preview_hunk)
map('n', '<leader>hb', function() gs.blame_line{full=true} end)
map('n', '<leader>tb', gs.toggle_current_line_blame)
map('n', '<leader>hd', gs.diffthis)
map('n', '<leader>hD', function() gs.diffthis('~') end)
map('n', '<leader>td', gs.toggle_deleted)
-- Text object
map({'o', 'x'}, 'ih', ':<C-U>Gitsigns select_hunk<CR>')
end
}
'';
};
in {
programs.neovim = {
plugins = [
my-gitsigns-nvim
];
};
}
hbac-nvim: auto close buffer
{ config, pkgs, stdenv, lib, ... }:
let
my-hbac = {
plugin = pkgs.vimUtils.buildVimPlugin {
name = "hbac.nvim";
src = pkgs.fetchFromGitHub {
owner = "axkirillov";
repo = "hbac.nvim";
rev = "e2e8333aa56ef43a577ac3a2a2e87bdf2f0d4cbb";
hash = "sha256-7+e+p+0zMHPJjpnKNkL7QQHZJGQ1DFZ6fsofcsVNXaY=";
};
doCheck = false;
};
type = "lua";
config = ''
require("hbac").setup({
autoclose = true, -- set autoclose to false if you want to close manually
threshold = 10, -- hbac will start closing unedited buffers once that number is reached
close_command = function(bufnr)
vim.api.nvim_buf_delete(bufnr, {})
end,
close_buffers_with_windows = false, -- hbac will close buffers with associated windows if this option is `true`
telescope = {
-- See #telescope-configuration below
},
})
'';
};
in {
programs.neovim = {
plugins = [
my-hbac
];
};
}
{ config, pkgs, stdenv, lib, ... }:
let
my-leap-nvim = {
plugin = pkgs.vimPlugins.leap-nvim;
type = "lua";
config = ''
require('leap').set_default_mappings()
'';
};
in {
programs.neovim = {
plugins = [
my-leap-nvim
];
};
}
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimPlugins.markdown-preview-nvim.overrideAttrs (old: {
postPatch = toString [
# use cjk2ch.js
(let
cjk2ch_js = pkgs.fetchurl {
url = "https://github.com/xieby1/markdown_revealjs/raw/c9c91257d06b63adcf67d3464efbc392f55677ff/share/markdown_revealjs/cjk2ch.js";
sha256 = "0d2xjy8g89zq8677ggklfpr3f9jd8lny37lbcdqr0asvmiqjgx9x";
};
# 添加cjk2ch.js
# 给混淆过的index.js打补丁(这生成代码居然直接放git,😼😼😼啧啧啧,源码是index.jsx)
# 通过code:not(:has(span))来识别哪些code可能是ascii art
# 在refreshContent最后,即renderDot的后面调用cjk2ch(...)
in ''
cp ${cjk2ch_js} app/_static/cjk2ch.js
sed -i 's,<head>,&<script src="/_static/cjk2ch.js"></script>,' app/out/index.html
sed -i 's,}var e;G(),;cjk2ch("code:not(:has(span))");&,' app/out/_next/static/s7O4q0ISzv1r8jtwIkXLb/pages/index.js
'')
# set port to 7700~7799
''
sed -i 's/port = port ||.*/port = port || (7700 + Number(`''${Date.now()}`.slice(-2)))/' app/server.js
''
# set line-height of <pre></pre>
''
sed -i 's/line-height: 1.45;/line-height: 1em;/' app/_static/markdown.css
''];
});
config = ''
" allow LAN
let g:mkdp_open_to_the_world = 1
let g:mkdp_theme = 'light'
" Use chromium-like browser to open rendered markdown instead of firefox,
" because firefox only copy css in selected region, causing lost of styles!
" See same bug report here:
" https://discourse.mozilla.org/t/copy-rich-text-from-firefox-does-not-capture-style/97862/3
function OpenMarkdownPreview (url)
" run chromium in app mode in background
execute "silent ! chromium --app='" . a:url . "' &"
endfunction
let g:mkdp_browserfunc = 'OpenMarkdownPreview'
'';
}];
};
}
mini-nvim: a nvim distro
mini-nvim is wonderful nvim plugin! I found it due to below link: indent-blankline.nvim is too complex. However, it does not support basic functionality like highlight current indentation See: https://github.com/lukas-reineke/indent-blankline.nvim/issues/649
{ config, pkgs, stdenv, lib, ... }:
let
my-mini-nvim = {
plugin = pkgs.vimPlugins.mini-nvim;
type = "lua";
config = ''
require('mini.indentscope').setup{
options = {
try_as_border = true,
},
}
-- mini.animate looks promising, and can totally replace vim-smoothie
-- However, bugs seem exist:
-- * touchpad scroll become slow
-- * background color blinks when create window
-- * background color broken after q::q
-- require('mini.animate').setup()
require('mini.icons').setup()
'';
};
in {
programs.neovim = {
plugins = [
my-mini-nvim
];
};
}
nvim-cmp: completion
{ config, pkgs, stdenv, lib, ... }:
let
my-nvim-cmp = {
plugin = pkgs.vimPlugins.nvim-cmp;
type = "lua";
config = ''
local cmp = require'cmp'
cmp.setup({
preselect = cmp.PreselectMode.None,
snippet = {
-- REQUIRED - you must specify a snippet engine
expand = function(args)
require('luasnip').lsp_expand(args.body) -- For `luasnip` users.
end,
},
mapping = cmp.mapping.preset.insert({
['<C-u>'] = cmp.mapping.scroll_docs(-4), -- Up
['<C-d>'] = cmp.mapping.scroll_docs(4), -- Down
-- C-b (back) C-f (forward) for snippet placeholder navigation.
['<C-Space>'] = cmp.mapping.complete(),
-- https://github.com/hrsh7th/nvim-cmp/issues/1753
-- The "Safely select entries with <CR>" example from wiki does not work correctly in command mode
["<CR>"] = cmp.mapping({
i = function(fallback)
if cmp.visible() and cmp.get_active_entry() then
cmp.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = false })
else
fallback()
end
end,
s = cmp.mapping.confirm({ select = true }),
c = cmp.mapping.confirm({ behavior = cmp.ConfirmBehavior.Replace, select = true }),
}),
['<Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_next_item()
else
fallback()
end
end, { 'i', 's' }),
['<S-Tab>'] = cmp.mapping(function(fallback)
if cmp.visible() then
cmp.select_prev_item()
else
fallback()
end
end, { 'i', 's' }),
}),
sources = cmp.config.sources({{
name = 'nvim_lsp',
}}, {{
name = 'luasnip',
}}, {{
name = 'buffer',
option = {
-- completion using words from visible buffers
get_bufnrs = function()
local bufs = {}
for _, win in ipairs(vim.api.nvim_list_wins()) do
bufs[vim.api.nvim_win_get_buf(win)] = true
end
return vim.tbl_keys(bufs)
end
},
}}, {{
name = 'path',
'' + pkgs.lib.optionalString (builtins.currentSystem=="x86_64-linux") ''
}}, {{
name = 'cmp_tabnine',
'' + ''
}})
})
'';
};
my-cmp-tabnine = {
plugin = pkgs.vimPlugins.cmp-tabnine;
type = "lua";
config = ''
local tabnine = require('cmp_tabnine.config')
tabnine:setup({
max_lines = 1000,
max_num_results = 20,
sort = true,
run_on_every_keystroke = true,
snippet_placeholder = '..',
ignored_file_types = {
-- default is not to ignore
-- uncomment to ignore in lua:
-- lua = true
},
show_prediction_strength = false
})
'';
};
in {
programs.neovim = {
plugins = with pkgs.vimPlugins; [
my-nvim-cmp
cmp-nvim-lsp
cmp-buffer
cmp-path
luasnip
cmp_luasnip
] ++ pkgs.lib.optional (builtins.currentSystem == "x86_64-linux") my-cmp-tabnine;
};
}
nvim-config-local: secure load local vim config
{ pkgs, ... }:
let
my-nvim-config-local = {
plugin = pkgs.vimPlugins.nvim-config-local;
type = "lua";
config = ''
require('config-local').setup {
config_files = { ".nvim.lua", ".nvimrc", ".exrc", ".vimrc" },
lookup_parents = true,
silent = true,
}
'';
};
in {
programs.neovim = {
plugins = [
my-nvim-config-local
];
};
}
bashls: bash language server
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require('lspconfig').bashls.setup{}
require('lspconfig')['bashls'].setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = with pkgs; [
nodePackages.bash-language-server
];
};
}
lspconfig for C/C++ language
{ pkgs, ...}: {
programs.neovim = {
C/C++
Why use clangd instead of ccls?
I encountered the problem below, when view https://github.com/xieby1/openc910 smart_run/logical/tb/sim_main1.cpp
LSP[ccls]: Error NO_RESULT_CALLBACK_FOUND: {
error = {
code = -32603,
message = "failed to index /home/xieby1/Codes/openc910/smart_run/work/fputc.c"
},
id = 1,
jsonrpc = "2.0"
}
After some searching, I found
GitHub: neovim: issue: lsp: NO_RESULT_CALLBACK_FOUND with ccls, rust-analyzer #15844
sapphire-arches found:
Something is causing the r-a LSP to send two replies with the same ID, see the attached log: lsp_debug.log
It would be nice for the neovim LSP to handle this more gracefully (not filling my screen with garbage and taking focus), but I do think the bug is in R-A here? The problem seems to be related to editor.action.triggerParameterHints?
No one try to fix the two-replies problem in ccls. However, nimaipatel recommanded clangd_extensions.
extraLuaConfig = ''
require('lspconfig').clangd.setup{
filetypes = { "c", "cc", "cpp", "c++", "objc", "objcpp", "cuda", "proto" }
}
require("clangd_extensions.inlay_hints").setup_autocmd()
require("clangd_extensions.inlay_hints").set_inlay_hints()
require('lspconfig')['clangd'].setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
plugins = [
pkgs.vimPlugins.clangd_extensions-nvim
];
extraPackages = with pkgs; [
clang-tools
];
};
}
nvim-lspconfig
{ pkgs, ... }: {
imports = [
./bash.nix
./c.nix
./html.nix
./lua.nix
./nix.nix
./python.nix
./typos.nix
./xml.nix
./markdown.nix
./harper.nix
];
programs.neovim = {
plugins = [{
plugin = pkgs.vimPlugins.nvim-lspconfig;
type = "lua";
Global mappings.
config = ''
-- See `:help vim.diagnostic.*` for documentation on any of the below functions
vim.keymap.set('n', '<space>e', vim.diagnostic.open_float)
vim.keymap.set('n', '[d', vim.diagnostic.goto_prev)
vim.keymap.set('n', ']d', vim.diagnostic.goto_next)
vim.keymap.set('n', '<space>q', vim.diagnostic.setloclist)
-- Use LspAttach autocommand to only map the following keys
-- after the language server attaches to the current buffer
vim.api.nvim_create_autocmd('LspAttach', {
group = vim.api.nvim_create_augroup('UserLspConfig', {}),
callback = function(ev)
-- Enable completion triggered by <c-x><c-o>
vim.bo[ev.buf].omnifunc = 'v:lua.vim.lsp.omnifunc'
-- Buffer local mappings.
-- See `:help vim.lsp.*` for documentation on any of the below functions
local opts = { buffer = ev.buf }
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts)
vim.keymap.set('n', 'gd', vim.lsp.buf.definition, opts)
vim.keymap.set('n', 'gr', vim.lsp.buf.references, opts)
vim.keymap.set('n', 'gi', vim.lsp.buf.implementation, opts)
vim.keymap.set('n', 'K', vim.lsp.buf.hover, opts)
vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, opts)
vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, opts)
vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, opts)
vim.keymap.set('n', '<space>wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, opts)
vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, opts)
vim.keymap.set('n', '<space>rn', vim.lsp.buf.rename, opts)
vim.keymap.set({ 'n', 'v' }, '<space>ca', vim.lsp.buf.code_action, opts)
vim.keymap.set('n', '<space>f', function() vim.lsp.buf.format { async = true } end, opts)
end,
})
'';
}];
};
}
lspconfig for plain text
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
local paths = {
-- vim.fn.stdpath("config") .. "/spell/ltex.dictionary.en-US.txt",
vim.fn.expand("%:p:h") .. "/.ltexdict",
}
local words = {}
for _, path in ipairs(paths) do
local f = io.open(path)
if f then
for word in f:lines() do
table.insert(words, word)
end
f:close()
end
end
require('lspconfig').ltex.setup{
settings = {
ltex = {
-- Supported languages:
-- https://valentjn.github.io/ltex/settings.html#ltexlanguage
-- https://valentjn.github.io/ltex/supported-languages.html#code-languages
language = "en-US", -- "zh-CN" | "en-US",
filetypes = { "bib", "gitcommit", "markdown", "org", "plaintex", "rst", "rnoweb", "tex", "pandoc" },
dictionary = {
['en-GB'] = words,
},
},
},
}
require('lspconfig')['ltex'].setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = with pkgs; [
ltex-ls
];
};
}
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require('lspconfig').harper_ls.setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = [
pkgs.harper
];
};
}
lspconfig for html
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require('lspconfig').html.setup{}
require('lspconfig')['html'].setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = with pkgs; [
vscode-langservers-extracted # html
];
};
}
lspconfig for plain text
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require('lspconfig').lua_ls.setup{}
require('lspconfig')['lua_ls'].setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = with pkgs; [
lua-language-server
];
};
}
{ pkgs, ... }: {
# markdown lsp candidates:
# * marksman
# * keeps throwing errors: MailboxProcessor.PostAndAsyncReply timed out.
# Which is marked solved by author in 2023
# * https://github.com/artempyanykh/marksman/issues/144
# * https://github.com/artempyanykh/marksman/issues/164
# Really solved?
# Martins3 reports this problem on aarch64
# * https://github.com/Martins3/My-Linux-Config/issues/180
# Recent same bug report: during heading editing:
# * https://github.com/artempyanykh/marksman/issues/408
# * Maybe this MailboxProcessor.PostAndAsyncReply bug only comes out in LARGE documents folder?
# * zk: no goto def support
# * markdown-oxide: https://oxide.md/Features+Index
# * no goto link support
# * no completion of headings in the current file
# * ...
programs.neovim = {
extraLuaConfig = ''
require('lspconfig').marksman.setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = [(
pkgs.marksman
)];
};
}
lspconfig for nix
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require('lspconfig').nixd.setup{}
require('lspconfig')['nixd'].setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = with pkgs; [
nixd
];
};
}
lspconfig for python
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require('lspconfig').pyright.setup{}
require('lspconfig')['pyright'].setup {
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = with pkgs; [
pyright
];
};
}
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require'lspconfig'.typos_lsp.setup{}
'';
extraPackages = [
pkgs.typos-lsp
];
};
}
{ pkgs, ... }: {
programs.neovim = {
extraLuaConfig = ''
require'lspconfig'.lemminx.setup{
capabilities = require("cmp_nvim_lsp").default_capabilities(),
}
'';
extraPackages = [
pkgs.lemminx
];
};
}
nvim-metals: Scala LSP
The JRE proxy need subtle configuration.
# If jump to definition not work, try
# * clean .metals/, ~/.bloop/, ~/.cache/mill
# * MetalsResetChoice: Build server selection: mill-bsp
{ config, pkgs, ... }:
let
jre_with_proxy = pkgs.callPackage ./jre_with_proxy.nix {
jre = pkgs.openjdk_headless;
proxyHost = "127.0.0.1";
proxyPort = toString config.proxyPort;
};
my-nvim-metals = {
plugin = pkgs.vimPlugins.nvim-metals;
type = "lua";
config = ''
-- lspconfig.metals.setup{}
local metals_config = require("metals").bare_config()
metals_config.find_root_dir_max_project_nesting = 2
metals_config.root_patterns = {
"build.sbt",
"build.sc",
".git",
}
metals_config.settings = {
showImplicitArguments = true,
excludedPackages = { "akka.actor.typed.javadsl", "com.github.swagger.akka.javadsl" },
serverProperties = {
"-Dhttps.proxyHost=127.0.0.1",
"-Dhttps.proxyPort=${toString config.proxyPort}",
"-Dhttp.proxyHost=127.0.0.1",
"-Dhttp.proxyPort=${toString config.proxyPort}",
},
-- see `:h metalsBinaryPath`, "Another setting for you crazy Nix kids." Hahaha!
metalsBinaryPath = "${pkgs.metals.override {jre = jre_with_proxy;}}/bin/metals",
javaHome = "${jre_with_proxy}",
showImplicitArguments = true,
showImplicitConversionsAndClasses = true,
showInferredType = true,
-- use mill in system, preventing the situation where I can compile but I cannot use metals
millScript = "mill",
}
metals_config.capabilities = require("cmp_nvim_lsp").default_capabilities()
-- Autocmd that will actually be in charging of starting the whole thing
local nvim_metals_group = vim.api.nvim_create_augroup("nvim-metals", { clear = true })
vim.api.nvim_create_autocmd("FileType", {
group = nvim_metals_group,
pattern = { "scala", "sbt" },
callback = function()
require("metals").initialize_or_attach(metals_config)
end,
})
'';
};
in {
programs.neovim = {
plugins = [
my-nvim-metals
];
extraPackages = [
(pkgs.coursier.override {
jre = jre_with_proxy;
})
];
};
# nvim-metals proxy for bloop
home.file.jvmopts = {
text = ''
-Dhttps.proxyHost=127.0.0.1
-Dhttps.proxyPort=${toString config.proxyPort}
-Dhttp.proxyHost=127.0.0.1
-Dhttp.proxyPort=${toString config.proxyPort}
'';
target = ".bloop/.jvmopts";
};
}
JRE with Proxy
Let JRE aware of proxy by default. This package can be used standalone, while my main usage is for nvim-metals lsp. Without proxy, the scala package update make me headached.
For usage example, see my nvim-metals config
{ runCommand
, writeShellScript
, jre
, lndir
, proxyHost
, proxyPort
}: let
java_with_proxy_sh = writeShellScript "java" ''
${jre}/bin/java -Dhttp.proxyHost=${proxyHost} -Dhttp.proxyPort=${proxyPort} -Dhttps.proxyHost=${proxyHost} -Dhttps.proxyPort=${proxyPort} "$@"
'';
in runCommand "jre_with_proxy" {} ''
mkdir -p $out
${lndir}/bin/lndir -silent ${jre} $out
rm $out/bin/java
ln -s ${java_with_proxy_sh} $out/bin/java
''
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimPlugins.nvim-navic;
type = "lua";
config = ''
require("nvim-navic").setup {
lsp = {
auto_attach = true,
},
}
-- https://unix.stackexchange.com/questions/224771/what-is-the-format-of-the-default-statusline
-- %<%f %h%w%m%r%=%-14.(%l,%c%V%) %P
vim.o.statusline = "%<%f %h%w%m%r " ..
-- set maximum width of nvim-navic str: winwidth-40
-- exec %{%...%} results in '%.xx(yyyy%)', where xx is the max width, yyyy is the navic str
"%{%'%.' .. (winwidth(0)-40) .. '(' .. v:lua.require'nvim-navic'.get_location() .. '%)'%}" ..
" %=%-8.(%l,%c%) %P"
'';
}{
plugin = pkgs.vimPlugins.nvim-navbuddy;
type = "lua";
config = ''
require("nvim-navbuddy").setup {
lsp = {
auto_attach = true,
},
mappings = {
["<S-Tab>"] = require("nvim-navbuddy.actions").parent(),
["<Tab>"] = require("nvim-navbuddy.actions").children(),
},
}
'';
}];
};
}
nvim-treesitter: languages parsing
{ config, pkgs, stdenv, lib, ... }:
let
d2-vim vs tree-sitter-d2
When edit a large d2 file using d2-vim, the cursor movement becomes lag. However, tree-sitter-d2 works fluently. So I replace the d2-vim with tree-sitter-d2.
tree-sitter-d2 = pkgs.tree-sitter.buildGrammar rec {
language = "d2";
# tree-sitter language version 14
version = "0.5.1";
src = pkgs.fetchFromGitHub {
owner = "ravsii";
repo = "tree-sitter-d2";
rev = "v${version}";
hash = "sha256-Ru+EAtnBl+Td4HxHPXLwcXOiFB/NbYPE5AhMNFyP2Kg=";
};
};
my-nvim-treesitter = {
# Available languages see:
# https://github.com/nvim-treesitter/nvim-treesitter
# see `pkgs.tree-sitter.builtGrammars.`
# with `tree-sitter-` prefix and `-grammar` suffix removed
plugin = pkgs.vimPlugins.nvim-treesitter.withPlugins (tree-sitter: [
tree-sitter.c
tree-sitter.cpp
tree-sitter.python
tree-sitter.markdown
tree-sitter.lua
tree-sitter-d2
]);
type = "lua";
config = ''
require 'nvim-treesitter.configs'.setup {
-- TODO: https://github.com/NixOS/nixpkgs/issues/189838
-- ensure_installed = {"c", "cpp", "python", "markdown"},
ensure_installed = {},
sync_install = false,
highlight = {
enable = true,
additional_vim_regex_highlighting = false,
disable = {
"nix",
},
},
}
'';
};
in {
programs.neovim = {
plugins = [
my-nvim-treesitter
];
};
}
nvim-window-picker
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimPlugins.nvim-window-picker;
type = "lua";
config = ''
require 'window-picker'.setup({
selection_chars = 'abcdefghijklmnopqrstuvwxyz',
picker_config = {
statusline_winbar_picker = {
selection_display = function(char, windowid)
return '[' .. char .. ']' .. vim.o.statusline
end,
},
},
show_prompt = false,
highlights = {
statusline = {
unfocused = {
fg = '#2c2e34',
bg = '#7f8490',
bold = true,
},
},
},
})
vim.keymap.set('n', '<C-j>', function()
vim.api.nvim_set_current_win( require('window-picker').pick_window() )
end)
vim.keymap.set('n', '<C-w>j', function()
vim.api.nvim_set_current_win( require('window-picker').pick_window() )
end)
'';
}];
};
}
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimPlugins.outline-nvim;
type = "lua";
config = ''
require("outline").setup({
outline_window = {
relative_width = false,
},
guides = {
enabled = false, -- I already have mini indentscope
},
symbol_folding = {
autofold_depth = 99,
},
keymaps = {
close = {}, -- disable close key
},
})
'';
}];
};
}
avante-nvim: AI
2025.1.27:使用中发现的问题:
- 没有:h
- 说了要去掉nui但仍然没有https://news.ycombinator.com/item?id=41353835
- 安装了dressing、nui、plenary(telescope都没问题)插件仍然不识别
简单看了代码avante checkhealth:lua/avante/utils/init.lua 居然要使用lazy。按claude.ai的说法 “啊,这是实际的源码实现,比我之前解释的要简单很多。让我详细解释一下这个函数:” 是不是就是因为简单,但是又没有强制要求安装lazy,遇到的问题?avante: require("avante.health").check() avante.nvim ~ - ERROR Missing required plugin: stevearc/dressing.nvim - ERROR Missing required plugin: MunifTanjim/nui.nvim - ERROR Missing required plugin: nvim-lua/plenary.nvim - OK Found required plugin: nvim-treesitter/nvim-treesitter - OK Found icons plugin (nvim-web-devicons or mini.icons)
{ lib, pkgs, ... }:
{
programs.neovim = {
plugins = with pkgs.pkgsu.vimPlugins; (lib.mkAfter [
nui-nvim
dressing-nvim
{
plugin = avante-nvim;
type = "lua";
config = ''
require('avante_lib').load()
require('avante').setup ({
provider = "deepseek",
vendors = {
deepseek = {
__inherited_from = "openai",
api_key_name = "DEEPSEEK_API_KEY",
endpoint = "https://api.deepseek.com",
model = "deepseek-chat",
},
},
})
'';
}
]);
};
}
distant-nvim: edit remote files with local nvim
不足:
- 文档不在源码里,没有:h
- 文档写的很不清晰,找了半天才知道:DistantConnect和:DistantOpen
- 现有nvim文件操作似乎都不能用,比如:Ex比如
ff等,都需要用它的:DistantOpen - 简单尝试了xiangshan项目的跳转,没办法正常使用。但是sshfs一切正常
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.pkgsu.vimPlugins.distant-nvim;
type = "lua";
config = ''
require('distant'):setup()
'';
}];
extraPackages = [
pkgs.pkgsu.distant
];
};
}
Deprecated: remote-sshfs.nvim: integrate sshfs into nvim
Reason of Deprecation: It is better to use sshfs CLI!
In my opinion, sshfs provides a better experience than distant.nvim
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimUtils.buildVimPlugin {
pname = "remote-sshfs.nvim";
version = "2025-2-10";
src = pkgs.fetchFromGitHub {
owner = "NOSDuco";
repo = "remote-sshfs.nvim";
rev = "03f6c40c4032eeb1ab91368e06db9c3f3a97a75d";
hash = "sha256-vFEIISxhTIGSl9LzDYHuEIkjLGkU0y5XhfWI/i5DgN4=";
};
};
type = "lua";
config = ''
require('telescope').load_extension 'remote-sshfs'
require('remote-sshfs').setup{
connections = {
ssh_configs = {
vim.fn.expand "$HOME" .. "/.ssh/config",
vim.fn.expand "$HOME" .. "/Gist/Config/ssh.conf",
"/etc/ssh/ssh_config",
},
},
}
'';
}];
extraPackages = [
pkgs.sshfs
];
};
}
smartyank-nvim: smart yank to clipboard
{ config, pkgs, stdenv, lib, ... }:
let
my-smartyank-nvim = {
plugin = pkgs.vimPlugins.smartyank-nvim;
type = "lua";
config = ''
require('smartyank').setup {
highlight = {
enabled = false, -- not enable highlight yanked text
},
validate_yank = function() return vim.v.operator == '"+y' end,
}
'';
};
in {
programs.neovim = {
plugins = [
my-smartyank-nvim
];
};
}
telescope-nvim
{ config, pkgs, stdenv, lib, ... }:
let
my-telescope-nvim = {
plugin = pkgs.vimPlugins.telescope-nvim;
config = ''
" search relative to file
"" https://github.com/nvim-telescope/telescope.nvim/pull/902
nnoremap ff <cmd>lua require('telescope.builtin').find_files({cwd=require'telescope.utils'.buffer_dir()})<cr>
nnoremap fF <cmd>lua require('telescope.builtin').find_files()<cr>
nnoremap fb <cmd>lua require('telescope.builtin').buffers()<cr>
nnoremap fh <cmd>lua require('telescope.builtin').help_tags()<cr>
nnoremap ft <cmd>lua require('telescope.builtin').treesitter()<cr>
nnoremap fc <cmd>lua require('telescope.builtin').command_history()<cr>
nnoremap fC <cmd>lua require('telescope.builtin').commands()<cr>
'';
};
# Problem: unable to fuzzy search parenthesis '('
# https://github.com/nvim-telescope/telescope-fzf-native.nvim/issues/141
my-telescope-fzf-native-nvim = {
plugin = pkgs.vimPlugins.telescope-fzf-native-nvim;
type = "lua";
config = ''
require('telescope').setup {
extensions = {fzf = {}},
defaults = {
layout_strategy = 'vertical',
layout_config = {
height = 0.95,
width = 0.95,
-- do not disable preview
preview_cutoff = 1,
},
},
}
require('telescope').load_extension('fzf')
'';
};
my-telescope-live-grep-args-nvim = {
plugin = pkgs.vimPlugins.telescope-live-grep-args-nvim;
type = "lua";
config = ''
require('telescope').load_extension("live_grep_args")
-- nnoremap fg <cmd>lua require('telescope.builtin').live_grep({cwd=require'telescope.utils'.buffer_dir()})<cr>
vim.keymap.set('n', 'fg', '<cmd>lua require("telescope").extensions.live_grep_args.live_grep_args({search_dirs={require"telescope.utils".buffer_dir()}})<cr>')
-- nnoremap fG <cmd>lua require('telescope.builtin').live_grep()<cr>
vim.keymap.set('n', 'fG', '<cmd>lua require("telescope").extensions.live_grep_args.live_grep_args()<cr>')
'';
};
in {
programs.neovim = {
plugins = [
my-telescope-nvim
my-telescope-fzf-native-nvim
pkgs.vimPlugins.plenary-nvim
my-telescope-live-grep-args-nvim
];
extraPackages = with pkgs; [
ripgrep
];
};
}
{ pkgs, ... }: {
programs.neovim = {
plugins = [(
pkgs.vimPlugins.venn-nvim.overrideAttrs (old: {
# ▲ ▼ ◀ ▶
postPatch = ''
sed -i 's/►/▶/' lua/venn/init.lua
sed -i 's/◄/◀/' lua/venn/init.lua
sed -i 's/►/▶/' src/primitives/box.lua.t
sed -i 's/◄/◀/' src/primitives/box.lua.t
'';
})
)];
};
}
vim-easy-align: A simple, easy-to-use Vim alignment plugin.
{ config, pkgs, stdenv, lib, ... }:
let
my-vim-easy-align = {
plugin = pkgs.vimPlugins.vim-easy-align;
config = ''
" Start interactive EasyAlign in visual mode (e.g. vipga)
xmap ga <Plug>(EasyAlign)
" Start interactive EasyAlign for a motion/text object (e.g. gaip)
nmap ga <Plug>(EasyAlign)
let g:easy_align_delimiters = {
\ '>': { 'pattern': '>>\|=>\|>' },
\ '/': {
\ 'pattern': '//\+\|/\*\|\*/',
\ 'delimiter_align': 'l',
\ 'ignore_groups': ['!Comment'] },
\ ']': {
\ 'pattern': '[\]]',
\ 'left_margin': 0,
\ 'right_margin': 0,
\ 'stick_to_left': 0
\ },
\ '[': {
\ 'pattern': '[\[]',
\ 'left_margin': 0,
\ 'right_margin': 0,
\ 'stick_to_left': 0
\ },
\ ')': {
\ 'pattern': '[)]',
\ 'left_margin': 0,
\ 'right_margin': 0,
\ 'stick_to_left': 0
\ },
\ '(': {
\ 'pattern': '[(]',
\ 'left_margin': 0,
\ 'right_margin': 0,
\ 'stick_to_left': 0
\ },
\ }
let g:easy_align_ignore_groups = [] " default: ['Comment', 'String']
'';
};
in {
programs.neovim = {
plugins = [
my-vim-easy-align
];
};
}
vim-floaterm: floating terminal
{ config, pkgs, stdenv, lib, ... }:
let
my-vim-floaterm = {
plugin = pkgs.vimPlugins.vim-floaterm;
config = ''
nmap <Leader>t :FloatermNew --cwd=<buffer><CR>
" let g:floaterm_keymap_new = '<Leader>t'
let g:floaterm_width = 0.8
let g:floaterm_height = 0.8
" Set floaterm window's background
hi Floaterm guibg=black
'';
};
in {
programs.neovim = {
plugins = [
my-vim-floaterm
];
};
}
vim-hexokinase: display colors
highlight color code
TLDR: vim-hexokinase isn't perfect but works.
It need works in termguicolors
mode.
It is better to choose a color scheme which is visualized in gui mode.
And it is very tricky to setting colors in termguicolors
& notermguicolors
the same, which is insane.
It would be convenient, if color code can be visualised in editor, especially in web programming. I found two candidates plugins to achieve this goal, vim-css-color, vim-hexokinase.
Vim-css-color is not compatible with tree-sitter, due to regex based highlight. See Github Issue: Neovim tree sitter support for details. Vim-css-color sometimes cannot render same text color. I need to scroll my vim viewport, then it may render color correctly.
Vim-hexokinase is good, but must depends on termguicolors
is turned on.
termguicolors
will enable 24-bit RGB color,
while originally vim uses Base16 color.
The result is the color theme you familiar with will be changed.
Here is the visual comparison between vim-css-color and vim-hexokinase. I copy these text as html from my vim.
vcc | vcc tgc | vh tgc |
---|---|---|
#ff0000 | #ff0000 | #ff0000 |
#ff1111 | #ff1111 | #ff1111 |
#ff2222 | #ff2222 | #ff2222 |
#ff3333 | #ff3333 | #ff3333 |
#ff4444 | #ff4444 | #ff4444 |
#ff5555 | #ff5555 | #ff5555 |
#ff6666 | #ff6666 | #ff6666 |
#ff7777 | #ff7777 | #ff7777 |
#ff8888 | #ff8888 | #ff8888 |
#ff9999 | #ff9999 | #ff9999 |
#ffaaaa | #ffaaaa | #ffaaaa |
#ffbbbb | #ffbbbb | #ffbbbb |
#ffcccc | #ffcccc | #ffcccc |
#ffdddd | #ffdddd | #ffdddd |
#ffeeee | #ffeeee | #ffeeee |
#ffffff | #ffffff | #ffffff |
#000000 | #000000 | #000000 |
#111111 | #111111 | #111111 |
#222222 | #222222 | #222222 |
#333333 | #333333 | #333333 |
#444444 | #444444 | #444444 |
#555555 | #555555 | #555555 |
#666666 | #666666 | #666666 |
#777777 | #777777 | #777777 |
#888888 | #888888 | #888888 |
#999999 | #999999 | #999999 |
#aaaaaa | #aaaaaa | #aaaaaa |
#bbbbbb | #bbbbbb | #bbbbbb |
#cccccc | #cccccc | #cccccc |
#dddddd | #dddddd | #dddddd |
#eeeeee | #eeeeee | #eeeeee |
#ffffff | #ffffff | #ffffff |
Vim-css-color with out termguicolors
cannot display color correctly (or say precisely),
if you dont believe your eye, see the source code of this page.
I think vim-hexokinase with a termguicolors
toggle is a acceptable compromise.
Toggle termguicolors
by :set termguicolors!
.
I personally prefer to assign <leader>c
to toggle termguicolors
.
cterm & gui
TLDR: I still haven't find a elegant solution to keep termguicolors
and notermguicolors
visually same.
neovim has 2 color schemes cterm & gui,
see :h cterm-colors
& :h gui-colors
.
Default sntax colors are different in these two schemes.
For example, :verbose highlight Comment
returns
Comment xxx ctermfg=14 guifg=#80a0ff
Last set from /nix/store/pr1pwjjsm3k45rwi3w0xh2296rpymjlz-neovim-unwrapped-0.5.1/share/nvim/runtime/syntax/syncolor.vim
which means ctermfg uses the 14th color in ANSI colors,
while guifg use a hex color code.
The detailed setting is located in ${VIMRUNTIME}/syntax/syncolor.vim
.
I create a new syncolor.vim based the default one, and modify the all ctermfg and guifg to same color name, like below. The colors in two schemes are still different.
if !exists("syntax_cmd") || syntax_cmd == "on"
" ":syntax on" works like in Vim 5.7: set colors but keep links
command -nargs=* SynColor hi <args>
command -nargs=* SynLink hi link <args>
else
if syntax_cmd == "enable"
" ":syntax enable" keeps any existing colors
command -nargs=* SynColor hi def <args>
command -nargs=* SynLink hi def link <args>
elseif syntax_cmd == "reset"
" ":syntax reset" resets all colors to the default
command -nargs=* SynColor hi <args>
command -nargs=* SynLink hi! link <args>
else
" User defined syncolor file has already set the colors.
finish
endif
endif
" Many terminals can only use six different colors (plus black and white).
" Therefore the number of colors used is kept low. It doesn't look nice with
" too many colors anyway.
" Careful with "cterm=bold", it changes the color to bright for some terminals.
" There are two sets of defaults: for a dark and a light background.
if &background == "dark"
SynColor Comment term=bold cterm=NONE ctermfg=Cyan ctermbg=NONE gui=NONE guifg=Cyan guibg=NONE
SynColor Constant term=underline cterm=NONE ctermfg=Magenta ctermbg=NONE gui=NONE guifg=Magenta guibg=NONE
SynColor Special term=bold cterm=NONE ctermfg=LightRed ctermbg=NONE gui=NONE guifg=LightRed guibg=NONE
SynColor Identifier term=underline cterm=bold ctermfg=Cyan ctermbg=NONE gui=bold guifg=Cyan guibg=NONE
SynColor Statement term=bold cterm=NONE ctermfg=Yellow ctermbg=NONE gui=NONE guifg=Yellow guibg=NONE
SynColor PreProc term=underline cterm=NONE ctermfg=LightBlue ctermbg=NONE gui=NONE guifg=LightBlue guibg=NONE
SynColor Type term=underline cterm=NONE ctermfg=LightGreen ctermbg=NONE gui=NONE guifg=LightGreen guibg=NONE
SynColor Underlined term=underline cterm=underline ctermfg=LightBlue gui=underline guifg=LightBlue
SynColor Ignore term=NONE cterm=NONE ctermfg=black ctermbg=NONE gui=NONE guifg=black guibg=NONE
else
SynColor Comment term=bold cterm=NONE ctermfg=DarkBlue ctermbg=NONE gui=NONE guifg=DarkBlue guibg=NONE
SynColor Constant term=underline cterm=NONE ctermfg=DarkRed ctermbg=NONE gui=NONE guifg=DarkBlue guibg=NONE
SynColor Special term=bold cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=DarkMagenta guibg=NONE
SynColor Identifier term=underline cterm=NONE ctermfg=DarkCyan ctermbg=NONE gui=NONE guifg=DarkCyan guibg=NONE
SynColor Statement term=bold cterm=NONE ctermfg=Brown ctermbg=NONE gui=bold guifg=Brown guibg=NONE
SynColor PreProc term=underline cterm=NONE ctermfg=DarkMagenta ctermbg=NONE gui=NONE guifg=DarkMagenta guibg=NONE
SynColor Type term=underline cterm=NONE ctermfg=DarkGreen ctermbg=NONE gui=NONE guifg=DarkGreen guibg=NONE
SynColor Underlined term=underline cterm=underline ctermfg=DarkMagenta gui=underline guifg=DarkMagenta
SynColor Ignore term=NONE cterm=NONE ctermfg=white ctermbg=NONE gui=NONE guifg=white guibg=NONE
endif
SynColor Error term=reverse cterm=NONE ctermfg=White ctermbg=Red gui=NONE guifg=White guibg=Red
SynColor Todo term=standout cterm=NONE ctermfg=Black ctermbg=Yellow gui=NONE guifg=Black guibg=Yellow
The Magenta is especially dazzling, and cannot change it by below tries.
Refers to Change Vim's terminal colors when termguicolors is set #2353, nvim-terminal-emulator-configuration,
let g:terminal_color_13 = '#AD7FA8' " Magenta
doesn't work.
Finally I choose to add a color-scheme manager, and choose a theme which has both cterm & gui color scheme.
{ config, pkgs, stdenv, lib, ... }:
let
my-vim-hexokinase = {
plugin = pkgs.vimPlugins.vim-hexokinase;
config = ''
let g:Hexokinase_highlighters = ['backgroundfull']
'';
};
in {
programs.neovim = {
plugins = [
my-vim-hexokinase
];
};
}
vim-mark: multi-color highlight
{ config, pkgs, stdenv, lib, ... }:
let
vim-ingo-library = pkgs.vimUtils.buildVimPlugin {
name = "vim-ingo-library";
src = pkgs.fetchFromGitHub {
owner = "inkarkat";
repo = "vim-ingo-library";
rev = "c93c33f803356b16bf4b4d122404d8251dc7b24d";
hash = "sha256-85h3S+5IjVIbTkYJasPXU+z/ALc9oT+MzdyjvTOhIwg=";
};
};
my-vim-mark = {
plugin = pkgs.vimUtils.buildVimPlugin {
name = "vim-mark";
src = pkgs.fetchFromGitHub {
owner = "inkarkat";
repo = "vim-mark";
rev = "76a25085d7c46f90fd5dfd7c43c3ba05b86ee192";
hash = "sha256-h2uaZ8dCUopNJ7fpf3BEoZNRk+25zqFukcHa3LomPSk=";
};
};
config = ''
" clear highlight created by vim-mark
nnoremap <leader><F3> :MarkClear<CR>
" show all marks
nnoremap <leader>M :Marks<CR>
'';
};
in {
programs.neovim = {
plugins = [
my-vim-mark
vim-ingo-library
];
};
}
{ pkgs, ... }: {
programs.neovim = {
plugins = [{
plugin = pkgs.vimPlugins.vim-matchup;
type = "lua";
config = ''
vim.g.matchup_matchparen_offscreen = { method = "popup" }
-- 不要加粗光标下的可匹配文字,不然写代码时常遇到的不协调感觉
-- 比如写{}时{是普通字体}是加粗字体,光标左移一下,{变成加粗}是普通字体。
vim.cmd("highlight MatchParenCur cterm=NONE gui=NONE")
''
# * [Incompatibility with nvim-cmp snippet completion #328](Incompatibility with nvim-cmp snippet completion #328)
# * [[Bug] Sniprun + matchup cause snippets to automatically be expanded in nvim-cmp #354](https://github.com/andymass/vim-matchup/issues/354)
# * [fix(*) incopatibility with blink-cmp as well as nvim-cmp #354 #382](https://github.com/andymass/vim-matchup/pull/382/files)
+ ''
require('cmp').event:on("menu_opened", function()
vim.b.matchup_matchparen_enabled = false
end)
require('cmp').event:on("menu_closed", function()
vim.b.matchup_matchparen_enabled = true
end)
'';
}];
};
}
winshift-nvim: rearrange windows
{ config, pkgs, stdenv, lib, ... }:
let
my-winshift-nvim = {
plugin = pkgs.vimPlugins.winshift-nvim;
type = "lua";
config = ''
-- Lua
require("winshift").setup({
highlight_moving_win = true, -- Highlight the window being moved
focused_hl_group = "Visual", -- The highlight group used for the moving window
-- moving_win_options = {
-- -- These are local options applied to the moving window while it's
-- -- being moved. They are unset when you leave Win-Move mode.
-- wrap = true,
-- cursorline = false,
-- cursorcolumn = false,
-- colorcolumn = "",
-- },
keymaps = {
disable_defaults = false, -- Disable the default keymaps
win_move_mode = {
["h"] = "left",
["j"] = "down",
["k"] = "up",
["l"] = "right",
["H"] = "far_left",
["J"] = "far_down",
["K"] = "far_up",
["L"] = "far_right",
["<left>"] = "left",
["<down>"] = "down",
["<up>"] = "up",
["<right>"] = "right",
["<S-left>"] = "far_left",
["<S-down>"] = "far_down",
["<S-up>"] = "far_up",
["<S-right>"] = "far_right",
},
},
---A function that should prompt the user to select a window.
---
---The window picker is used to select a window while swapping windows with
---`:WinShift swap`.
---@return integer? winid # Either the selected window ID, or `nil` to
--- indicate that the user cancelled / gave an invalid selection.
window_picker = function()
return require("winshift.lib").pick_window({
-- A string of chars used as identifiers by the window picker.
picker_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890",
filter_rules = {
-- This table allows you to indicate to the window picker that a window
-- should be ignored if its buffer matches any of the following criteria.
cur_win = true, -- Filter out the current window
floats = true, -- Filter out floating windows
filetype = {}, -- List of ignored file types
buftype = {}, -- List of ignored buftypes
bufname = {}, -- List of vim regex patterns matching ignored buffer names
},
---A function used to filter the list of selectable windows.
---@param winids integer[] # The list of selectable window IDs.
---@return integer[] filtered # The filtered list of window IDs.
filter_func = nil,
})
end,
})
vim.keymap.set('n', '<C-W>m', '<Cmd>WinShift<CR>')
'';
};
in {
programs.neovim = {
plugins = [
my-winshift-nvim
];
};
}
usr/default.nix
home-manager的入口配置文件,它导入了:
因为部分系统不需要GUI程序,比如安卓手机nix-on-droid或是树莓派等。
我利用环境变量DISPLAY
用于判断是否导入GUI程序的配置。
{ ... }: {
imports = [
../modules
./modules/cachix.nix
./cli
./gui
];
news.display = "silent";
}
{ config, pkgs, stdenv, lib, ... }:
let
xelfviewer = pkgs.callPackage ./xelfviewer.nix {};
in
{
imports = [
./mime.nix
./kdeconnect.nix
./xdot.nix
./firefox
./warpd.nix
./rustdesk.nix
./gnome
./kitty
./singleton_web_apps.nix
./rofi.nix
./xcolor.nix
./wsl.nix
./drawio.nix
];
config = lib.mkIf config.isGui {
home.packages = with pkgs; [
libnotify
# browser
# dont ask me for keyring chromium-like browsers!
(chromium.override {commandLineArgs="--password-store=basic";})
] ++ [
# network
] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
feishu
(wechat-uos.override {
buildFHSEnv = args: buildFHSEnv (args // {
# bubble wrap wechat-uos's home directory
extraBwrapArgs = [
"--bind ${config.home.homeDirectory}/.local/share/wechat-uos /home"
"--chdir /home"
];
});
})
# wine weixin waste too much memory, more than 4GB!!!
#(import ./weixin.nix {})
nur.repos.linyinfeng.wemeet
nur.repos.xddxdd.dingtalk
# telegram desktop not provide aarch64 prebuilt
tdesktop
] ++ [
transmission_4-gtk
# text
#wpsoffice
libreoffice
meld
# TODO: use this after switching to wayland
#wl-clipboard
textsnatcher
# draw
#aseprite-unfree
inkscape
gimp
# viewer
] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
imhex
xelfviewer
] ++ [
vlc
obsidian
] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
ghidra
] ++ [
# management
] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
zotero
] ++ [
# pkgs.pkgsu.deskflow
barrier
keepassxc
# entertainment
antimicrox
];
# home.file.autostart_deskflow = {
# source = "${pkgs.pkgsu.deskflow}/share/applications/org.deskflow.deskflow.desktop";
# target = ".config/autostart/org.deskflow.deskflow.desktop";
# };
# use barrier due to its support of hotkey toggle screen!
# see: https://github.com/deskflow/deskflow/issues/8006
home.file.autostart_barrier = {
source = "${pkgs.barrier}/share/applications/barrier.desktop";
target = ".config/autostart/barrier.desktop";
};
};}
{ pkgs, ... }: {
home.packages = [
pkgs.drawio
];
xdg.mime.types = {
drawio = {
name = "draw-io";
type = "text/draw-io";
pattern = "*.drawio";
defaultApp = "drawio.desktop";
};
};
programs.neovim = {
extraConfig = ''
augroup filetype
au! BufRead,BufNewFile *.drawio set filetype=xml
augroup END
'';
};
}
安装deb包,以飞书为例
太长不看
- nix文件: https://github.com/xieby1/nix_config/blob/main/usr/gui/feishu.nix
- 使用方法:
home.packages = with pkgs; [ (callPackage ./feishu.nix {}) ];
参考nixpkgs/pkgs/applications/networking/instant-messengers/skypeforlinux/default.nix
测试环境
nix-build -E "with import
权限问题
nix-build遇到dpkg -x解压失败。 但是手动执行dpkg -x一切正常。
https://unix.stackexchange.com/questions/138188/easily-unpack-deb-edit-postinst-and-repack-deb
确认是权限问题的实验
是s权限的问题。 nix-build下不能添加s权限。 原因未知
复现方法
touch $out/miao
chmox +s $out/miao
ls -l $out/miao
解决方法,使用fakeroot.
补全库
使用ldd脚本,获取所有elf所需的库,挨个添加进rpath即可。
桌面
application/.desktop menu/.menu // 负责图标
firefox-apps
Comparison of chromium-based browsers and firefox
- list windows:
wmctrl -l
- list windows properties:
xprop -name <WIN_NAME> [PROP]
second instance of chrome ignores --class
- bing: chrome wm_class new instance
- https://superuser.com/questions/1457060/how-can-you-start-two-chrome-windows-with-different-wm-class-attributes
- https://issues.chromium.org/issues/40172351
- 目前看来最好的方法:--user-data-dir
- firefox类似,用不同的profile
- https://superuser.com/questions/1457060/how-can-you-start-two-chrome-windows-with-different-wm-class-attributes
https://superuser.com/questions/1770285/custom-firefox-profiles-with-different-wm-class-and-icons
set WM_CLASS
- https://stackoverflow.com/questions/36650865/set-wm-class-with-wnck-xprop-or-something-else 能正常设置kitty的WM_CLASS,但是为什么chrome不行?
所以选择firefox!
{ config, pkgs, ... }: {
imports = [
./module.nix
];
firefox-apps = [{
name = "ms-todo";
url = "https://to-do.live.com/";
desktopEntryExtras = {
name = "Microsoft To Do";
};
keybinding = "<Super>t";
}{
name = "ms-calendar"; # avoid singleton xdotool conflicts with system calendar
url = "https://outlook.live.com/calendar";
desktopEntryExtras = {
name = "Microsoft Calendar";
genericName = "outlook calendar mail";
};
keybinding = "<Super>c";
}{
name = "webweixin";
url = "https://wx.qq.com";
desktopEntryExtras = {
name = "网页微信";
genericName = "weixin";
};
}{
name = "my_cheatsheet_html";
url = "${config.home.homeDirectory}/Documents/Tech/my_cheatsheet.html";
keybinding = "<Super>space";
desktopEntryExtras = {
name = "Cheatsheet HTML";
genericName = "cheatsheet";
icon = builtins.toFile "cheatsheet.svg" ''
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" rx="20%" ry="20%" fill="#666666"/>
<text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" font-size="18" font-weight="bold" fill="#F5F5F5">
<tspan x="50%" dy="-0.6em">CS</tspan>
<tspan x="50%" dy="1.2em">html</tspan>
</text>
</svg>
'';
};
}{
name = "bing_dict";
url = "https://cn.bing.com/dict";
keybinding = "<Super>b";
desktopEntryExtras = {
name = "Bing Dict";
genericName = "dictionary";
};
}{
name = "riyucidian";
url = "https://www.mojidict.com";
keybinding = "<Super>j";
desktopEntryExtras = {
name = "日语词典";
};
}{
name = "devdocs";
url = "https://devdocs.io";
desktopEntryExtras = {
name = "DevDocs";
};
}(let
metacubexd = builtins.fetchTarball "https://github.com/MetaCubeX/metacubexd/archive/gh-pages.zip";
in {
name = "clash";
url = "${metacubexd}/index.html";
desktopEntryExtras = {
icon = "${metacubexd}/pwa-192x192.png";
};
}){
name = "ai";
url = "https://web.chatboxai.app";
desktopEntryExtras = {
name = "AI Chatbox";
};
}{
name = "deepseek";
url = "https://chat.deepseek.com";
keybinding = "<Super>a";
desktopEntryExtras = {
name = "DeepSeek";
};
}{
name = "doubao";
url = "https://www.doubao.com/chat";
desktopEntryExtras = {
name = "豆包";
};
}{
name = "spotify";
url = "https://open.spotify.com/search";
desktopEntryExtras = {
name = "Spotify (Search)";
};
}{
name = "syncthing";
url = "http://127.0.0.1:8384";
icon = "${pkgs.syncthingtray-minimal}/share/icons/hicolor/scalable/apps/syncthingtray.svg";
}];
}
firefox module
{ config, pkgs, lib, ... }: let
singleton = pkgs.writeShellScript "singleton.sh" ''
if [[ $# -lt 2 || $1 == "-h" ]]
then
echo "Usage: ''${0##*/} <wmclass> <command and its args>"
echo " Only start a app once, if the app is running"
echo " then bring it to foreground"
exit 0
fi
WID=$(${pkgs.xdotool}/bin/xdotool search --class "$1")
if [[ -z $WID ]]; then
eval "''${@:2}"
else
for WIN in $WID; do
CURDESK=$(${pkgs.xdotool}/bin/xdotool get_desktop)
${pkgs.xdotool}/bin/xdotool set_desktop_for_window $WIN $CURDESK
${pkgs.xdotool}/bin/xdotool windowactivate $WIN
done
fi
'';
in {
options = {
firefox-apps = lib.mkOption {
type = lib.types.listOf (lib.types.submodule {
options = {
name = lib.mkOption {
type = lib.types.str;
default = null;
};
url = lib.mkOption {
type = lib.types.str;
default = null;
};
icon = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
};
desktopEntryExtras = lib.mkOption {
type = lib.types.attrs;
default = {};
description = "extra settings for desktopEntry, see xdg.desktopEntries.<xxx>";
};
keybinding = lib.mkOption {
default = null;
};
};
});
};
};
config = {
programs.firefox.profiles = builtins.listToAttrs (
lib.imap1 (i: firefox-app: {
name = assert firefox-app.name!=null; firefox-app.name;
value = {
id = i;
extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
# auto open all windows in fullscreen mode
i-auto-fullscreen
darkreader
vimium
ublock-origin
];
settings = {
# https://superuser.com/questions/1483037/making-firefox-fullscreen-like-without-actually-maximizing-the-window
# the full screen hotkey/button will trigger fullscreen like normal, except it won't resize the window.
"full-screen-api.ignore-widgets" = true;
# Automatically enable extensions
"extensions.autoDisableScopes" = 0;
# https://stackoverflow.com/questions/51081754/cross-origin-request-blocked-when-loading-local-file
"security.fileuri.strict_origin_policy" = false;
# enable userChrome.css
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
# disable extensions auto update
"extensions.update.enabled" = false;
};
userChrome = ''
/* [Disabling the mouseover to reveal the address/toolbar while in fullscreen - old method doesn't work](https://support.mozilla.org/en-US/questions/1324666) */
/* [prevent firefox from showing the address bar in fullscreen mode](https://support.mozilla.org/en-US/questions/1323320) */
*|div#fullscr-toggler {display:none!important;}
'';
};
}) config.firefox-apps
);
xdg.desktopEntries = builtins.listToAttrs (
map (firefox-app: {
name = assert firefox-app.name!=null; firefox-app.name;
value = {
name = firefox-app.name;
genericName = firefox-app.name;
exec = assert firefox-app.url!=null;
"firefox -P ${firefox-app.name} --class ${firefox-app.name} ${firefox-app.url}";
icon = if firefox-app.icon != null then firefox-app.icon
else builtins.fetchurl {
url = "http://www.google.com/s2/favicons?domain=${firefox-app.url}&sz=128";
name = "${firefox-app.name}.png";
};
settings = {
StartupWMClass = firefox-app.name;
};
} // firefox-app.desktopEntryExtras;
}) config.firefox-apps
);
dconf.settings = let
firefox-apps-with-keybinding = (builtins.filter (firefox-app: firefox-app.keybinding!=null ) config.firefox-apps);
in {
"org/gnome/settings-daemon/plugins/media-keys".custom-keybindings = map (
firefox-app: "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/${firefox-app.name}/"
) firefox-apps-with-keybinding;
} // (builtins.listToAttrs (
map (firefox-app: {
name = "org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/${firefox-app.name}";
value = {
binding = firefox-app.keybinding;
command = "${singleton} ${firefox-app.name} gtk-launch ${firefox-app.name}.desktop";
name = firefox-app.name;
};
}) firefox-apps-with-keybinding
));
};
}
firefox configurations
{ pkgs, ... }:
{
imports = [
./apps
./extensions
];
programs.firefox = {
enable = true;
# If state version ≥ 19.09 then this should be a wrapped Firefox
package = pkgs.firefox.overrideAttrs (old: {
# MOZ_USE_XINPUT2=1 allow more smooth (pixel-level) scroll and zoom
buildCommand = old.buildCommand + ''
mv $out/bin/firefox $out/bin/firefox-no-xinput2
makeWrapper $out/bin/firefox-no-xinput2 $out/bin/firefox --set-default MOZ_USE_XINPUT2 1
'';
});
# id is default 0, thus this profile is default
profiles.xieby1 = {
settings = {
# https://superuser.com/questions/1483037/making-firefox-fullscreen-like-without-actually-maximizing-the-window
# the full screen hotkey/button will trigger fullscreen like normal, except it won't resize the window.
"full-screen-api.ignore-widgets" = true;
# Disable `alt` key of toggling menu bar
"ui.key.menuAccessKeyFocuses" = false;
"ui.key.menuAccessKey" = -1; # original number 18
# enable userChrome.css
"toolkit.legacyUserProfileCustomizations.stylesheets" = true;
};
userChrome =
# [Disabling the mouseover to reveal the address/toolbar while in fullscreen - old method doesn't work](https://support.mozilla.org/en-US/questions/1324666)
# [prevent firefox from showing the address bar in fullscreen mode](https://support.mozilla.org/en-US/questions/1323320)
''
*|div#fullscr-toggler {display:none!important;}
'';
};
};
}
{ pkgs, ... }: {
programs.firefox = {
profiles.xieby1 = {
extensions.packages = [ pkgs.nur.repos.rycee.firefox-addons.brotab ];
};
};
home.packages = [ pkgs.brotab ];
home.file.brotab_mediator = rec {
target = ".mozilla/native-messaging-hosts/brotab_mediator.json";
source = pkgs.runCommand "brotab_mediator" {} ''
export HOME=.
${pkgs.brotab}/bin/brotab install
cp ${target} $out
'';
};
}
{ pkgs, ... }: let
darkreader = pkgs.nur.repos.rycee.firefox-addons.darkreader;
in {
programs.firefox = {
profiles.xieby1 = {
extensions.packages = [ darkreader ];
};
};
firefox-extensions.xieby1.browser-extension-data."${darkreader.addonId}" = {
storage = {
schemeVersion = 2;
syncSettings = false;
disabledFor = [
# markdown-preview-nvim: x.x.x.x:7768
# markdown_revealjs daemon: x.x.x.x:7782
"/.*:77[0-9][0-9]/"
# markdown_revealjs
"//home/.*/slides/index.html/"
];
};
};
}
{ pkgs, ... }: {
imports = [
./module.nix
./sidebery.nix
./darkreader.nix
./smartproxy.nix
./smart-toc.nix
./brotab.nix
];
programs.firefox = {
profiles.xieby1 = {
extensions.packages = with pkgs.nur.repos.rycee.firefox-addons; [
# 😾😾😾 Chinese users cannot use ad block extensions
# https://discourse.mozilla.org/t/chinese-users-cant-use-ad-blocker-extensions/94823
ublock-origin
vimium
];
settings = {
# Automatically enable extensions
"extensions.autoDisableScopes" = 0;
};
};
};
}
{ config, pkgs, lib, ... }: {
options = {
firefox-extensions = lib.mkOption {
# per-profile
type = lib.types.attrsOf (lib.types.submodule { options = {
browser-extension-data = lib.mkOption {
# per-extension
type = lib.types.attrsOf (lib.types.submodule { options = {
storage = lib.mkOption {
type = lib.types.attrs;
default = {};
description = ''
Attrs that will be converted to json and merged to storage.js
'';
};
};});
};
extension-settings = lib.mkOption {
type = lib.types.mkOptionType {
name = "attrsRecursiveUpdate";
description = "attribute set";
check = lib.isAttrs;
merge = loc: lib.foldl' (res: def: lib.recursiveUpdate res def.value) { };
emptyValue = {
value = { };
};
};
default = {};
description = ''
The extension-settings.json
'';
};
};});
default = {};
description = ''
Attrs of firefox-extensions settings
You may ask: why not using `programs.firefox.profiles.<name>.extensions.settings`?
Because it do not support partially updating the settings.
Example:
firefox-extensions = {
profile0 = {
browser-extension-data = {
darkreader = {
storage = {
schemeVersion = 2;
syncSettings = false;
disabledFor = [
"http://127.0.0.1:7768"
];
};
};
};
extension-settings = {
...
};
};
profile1 = {...};
};
'';
};
};
config = {
home.file = (builtins.listToAttrs (lib.flatten (
lib.mapAttrsToList (profile: per-profile-settings: (
lib.mapAttrsToList (extensionId: per-extension-settings: {
name = "firefox-${profile}-${extensionId}";
value = let
relDir = "${config.programs.firefox.configPath}/${profile}/browser-extension-data/${extensionId}";
absDir = "${config.home.homeDirectory}/${relDir}";
in {
target = "${relDir}/_storage_.js";
text = builtins.toJSON per-extension-settings.storage;
onChange = ''
if [[ -e ${absDir}/storage.js ]]; then
${pkgs.yq-go}/bin/yq -i ea '. as $item ireduce ({}; . * $item )' ${absDir}/storage.js ${absDir}/_storage_.js
else
cat ${absDir}/_storage_.js > ${absDir}/storage.js
fi
'';
};
}) per-profile-settings.browser-extension-data
)) config.firefox-extensions
))) // (
lib.mapAttrs' (profile: per-profile-settings: {
name = "firefox-${profile}-extension-settings";
value = let
relDir = "${config.programs.firefox.configPath}/${profile}";
absDir = "${config.home.homeDirectory}/${relDir}";
in {
target = "${relDir}/_extension-settings_.json";
text = builtins.toJSON ({
version = 3;
} // per-profile-settings.extension-settings);
onChange = ''
if [[ -e ${absDir}/extension-settings.json ]]; then
${pkgs.yq-go}/bin/yq -i ea '. as $item ireduce ({}; . * $item )' ${absDir}/extension-settings.json ${absDir}/_extension-settings_.json
else
cat ${absDir}/_extension-settings_.json > ${absDir}/extension-settings.json
fi
'';
};
}) config.firefox-extensions
);
programs.firefox.profiles = lib.mapAttrs (profile: _: {
settings = {
"extensions.webextensions.ExtensionStorageIDB.enabled" = false;
};
}) config.firefox-extensions;
};
}
{ pkgs, ... }: let
sidebery = pkgs.nur.repos.rycee.firefox-addons.sidebery;
in {
programs.firefox = {
profiles.xieby1 = {
extensions.packages = [ sidebery ];
# transparent floating sidebar
# Why not use it?
# Because: I tried two ways to adjust the height of sidebar dynamically, but all failed:
# * user.js cannot inject js code
# * css counter cannot used in height
#
#settings = {
# "browser.tabs.allow_transparent_browser" = true;
#};
#userChrome = ''
# #sidebar-box {
# height: 90%;
# top: 5%;
# right: 0;
# position: absolute;
# z-index: 99;
# }
# #browser,
# tabbox#tabbrowser-tabbox,
# #tabbrowser-tabpanels,
# #sidebar-box,
# #sidebar,
# window#webextpanels-window {
# background-color: transparent !important;
# background-image: none !important;
# }
#'';
#userContent = ''
# @-moz-document regexp("^moz-extension://.*/sidebar/sidebar.html") {
# :root {
# background-color: transparent !important;
# }
# #root.root {
# --frame-bg: transparent !important;
# --toolbar-bg: black !important;
# --frame-el-bg: black !important;
# }
# }
#'';
settings = {
"sidebar.position_start" = false; # sidebar on the right
};
userChrome = ''
/* Completely Remove Firefox Tab Bar */
/* https://bricep.net/completely-remove-firefox-tab-bar/ */
#TabsToolbar { visibility: collapse !important; }
#sidebar-header { display: none; }
#sidebar-splitter { display: none; }
#sidebar-box {
width: 13vw !important;
min-width: 8em !important;
max-width: 15em !important;
padding: 0 !important;
}
'';
};
};
firefox-extensions.xieby1 = {
extension-settings = {
commands = {
# toggle sidebery
_execute_sidebar_action = { precedenceList = [{
id = sidebery.addonId;
installDate = 0;
value = { shortcut = "F1"; };
enabled = true;
}];};
search = { precedenceList = [{
id = sidebery.addonId;
installDate = 0;
value = { shortcut = "F2"; };
enabled = true;
}];};
up_shift = { precedenceList = [{
id = sidebery.addonId;
installDate = 0;
value = { shortcut = ""; };
enabled = true;
}];};
down_shift = { precedenceList = [{
id = sidebery.addonId;
installDate = 0;
value = { shortcut = ""; };
enabled = true;
}];};
switch_to_next_tab = { precedenceList = [{
id = sidebery.addonId;
installDate = 0;
value = { shortcut = "Alt+Shift+Down"; };
enabled = true;
}];};
switch_to_prev_tab = { precedenceList = [{
id = sidebery.addonId;
installDate = 0;
value = { shortcut = "Alt+Shift+Up"; };
enabled = true;
}];};
};
};
};
}
{ pkgs, lib, ... }: let
smart-toc = pkgs.nur.repos.rycee.firefox-addons.buildFirefoxXpiAddon {
pname = "smart-toc";
version = "0.11.25";
addonId = "{40289693-01fe-4740-91ae-07344bf5b09b}";
url = "https://addons.mozilla.org/firefox/downloads/file/4485577/smart_toc-0.11.25.xpi";
sha256 = "17jpqy2qs3qc1m9l4kbqw14m5bkwfzb253wpf1x1b5p9q6sap8w8";
meta = with lib; {
mozPermissions = [
"activeTab"
"scripting"
"storage"
"tabs"
"*://*/*"
"file:///*"
];
platforms = platforms.all;
};
};
in {
programs.firefox = {
profiles.xieby1 = {
extensions.packages = [ smart-toc ];
};
};
firefox-extensions.xieby1 = {
extension-settings = {
commands = {
toggle = {precedenceList = [{
id = smart-toc.addonId;
installDate = 0;
value = { shortcut = "Alt+E"; };
enabled = true;
}];};
};
};
};
}
{ config, pkgs, ... }: let
inherit (pkgs.nur.repos.rycee.firefox-addons) smartproxy;
in {
programs.firefox = {
profiles.xieby1 = {
extensions.packages = [ smartproxy ];
};
};
firefox-extensions.xieby1 = {
browser-extension-data."${smartproxy.addonId}" = {
storage = {
activeProfileId = "InternalProfile_SmartRules";
defaultProxyServerId = "defaultProxyServerId";
proxyServers = [{
name = "nix";
id = "defaultProxyServerId";
order = 0;
host = "127.0.0.1";
port = config.proxyPort;
protocol = "HTTP";
username = "";
password = "";
proxyDNS = true;
failoverTimeout = null;
}];
proxyProfiles = [{
profileName = "Smart Proxy";
profileId = "InternalProfile_SmartRules";
profileProxyServerId = "defaultProxyServerId";
profileType = 2;
enabled = true;
rulesSubscriptions = [{
enabled = true;
refreshRate = 6000;
id = "gfwlist";
name = "gfwlist";
url = "https://raw.githubusercontent.com/gfwlist/gfwlist/master/gfwlist.txt";
obfuscation = "Base64";
format = 0;
applyProxy = 1;
username = "";
password = "";
}];
profileTypeConfig = {
builtin = true;
editable = true;
selectable = true;
supportsSubscriptions = true;
supportsProfileProxy = true;
customProxyPerRule = true;
canBeDisabled = true;
supportsRuleActionWhitelist = true;
defaultRuleActionIsWhitelist = false;
};
}];
};
};
};
}
{ config, ... }: {
imports = [
./module.nix
{ # my private calendars
gnome-calendar = let
caldavs = "${config.home.homeDirectory}/Gist/Vault/caldavs.nix";
in if builtins.pathExists caldavs then import caldavs else {};
}
];
# public calendars
gnome-calendar = {
china_calendar = {
# 包含所有类型日历(暂时除农历、天干地支)(时间段版本)
url = "https://yangh9.github.io/ChinaCalendar/calendar_1.ics";
settings = {
Calendar = {
Color = "#82B366";
};
# do not show notifications for this calendar
Alarms = {
IncludeMe = false;
ForEveryEvent = false;
};
};
};
};
}
gnome-calendar module
{ config, pkgs, lib, ... }: {
options = {
gnome-calendar = lib.mkOption {
# entry type
type = lib.types.attrsOf (lib.types.submodule {
options = {
url = lib.mkOption {
type = lib.types.str;
default = "";
};
settings = lib.mkOption {
type = lib.types.attrs;
default = {};
};
};
});
default = {};
description = ''
Attrs of calendars settings.
gnome-calendar = {
calendar1 = {
url = "...calendar1.ics";
settings = {...calendar1 settings...}
};
calendar2 = {
url = "...calendar2.ics";
settings = {...calendar2 settings...}
};
...
};
'';
};
};
config = {
# Available options: <evolution-data-server>/glib-2.0/schemas/org.gnome.evolution-data-server.calendar.gschema.xml
dconf.settings."org/gnome/evolution-data-server/calendar" = {
# Currently, cannot use audio, otherwise no notification pops out.
# * https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1226
# * https://gitlab.gnome.org/GNOME/gnome-calendar/-/issues/1280
notify-enable-audio = false;
notify-enable-display = true;
# Default reminder for all events in chosen calendars
defall-reminder-enabled = true;
# defall-reminder-interval = 15
# defall-reminder-units = minutes
};
home.file = lib.mapAttrs (name: entry:
let
parseUrl = urlString:
let
protocolSplit = lib.strings.splitString "://" urlString;
protocol = builtins.head protocolSplit;
afterProtocol = builtins.elemAt protocolSplit 1;
hostPathSplit = lib.strings.splitString "/" afterProtocol;
host = builtins.head hostPathSplit;
path = "/" + lib.strings.concatStringsSep "/" (builtins.tail hostPathSplit);
in {
inherit protocol host path;
port = if protocol == "http" then 80
else if protocol == "https" then 443
else throw "unknown protocol: ${protocol}";
security = if protocol == "https" then "tls"
else "";
};
parsedUrl = parseUrl entry.url;
mergeCfg = lib.foldl lib.recursiveUpdate {};
cal_cfg = mergeCfg [
(import ./template.nix)
{
"Data Source" = {
DisplayName = name;
};
Authentication = {
Host=parsedUrl.host;
Port=parsedUrl.port;
};
Security = {
Method=parsedUrl.security;
};
"WebDAV Backend" = {
ResourcePath=parsedUrl.path;
};
}
entry.settings
];
src = (pkgs.formats.ini {}).generate "${name}.source" cal_cfg;
dst = "${config.home.homeDirectory}/.config/evolution/sources/${name}.source";
# Why not using yq, due to Color=#xxxxxx, '#' will be identified as comment, and be striped
in rec {
target = ".config/evolution/sources/${name}.source.onChange";
source = (pkgs.formats.ini {}).generate "${name}.source" cal_cfg;
onChange = ''
sed 's/LastNotified=.*/LastNotified='$(date -u +"%Y-%m-%dT%H:%M:%SZ")'/g' \
${target} > ${lib.removeSuffix ".onChange" target}
'';
}
) config.gnome-calendar;
};
}
{
"Data Source" = {
DisplayName = "toBeFilled";
Enabled=true;
Parent="webcal-stub";
};
Authentication = {
Host= "toBeFilled";
Method="plain/password";
Port=443;
ProxyUid="system-proxy";
RememberPassword=true;
User="";
CredentialName="";
IsExternal=false;
};
Security = {
Method="tls";
};
"WebDAV Backend" = {
AvoidIfmatch=false;
CalendarAutoSchedule=false;
Color="";
DisplayName="";
EmailAddress="";
ResourcePath= "toBeFilled";
ResourceQuery="";
SslTrust="";
Order=4294967295;
Timeout=90;
};
Calendar = {
BackendName="webcal";
Color = "#B85450";
Selected=true;
Order=0;
};
Offline = {
StaySynchronized=true;
};
Refresh = {
Enabled=true;
EnabledOnMeteredNetwork=true;
IntervalMinutes=30;
};
Alarms = {
IncludeMe=true;
# LastNotified="2025-02-24T13:27:00Z";
LastNotified="";
ForEveryEvent=true;
};
}
Gnome settings
{ config, pkgs, lib, ... }: {
imports = [
./calendar
./extensions
];
home.packages = with pkgs; [
gnome-sound-recorder
dconf-editor
devhelp
];
# Setting: `gsettings set <key(dot)> <value>`
# Getting: `dconf dump /<key(path)>`
dconf.settings = {
"org/gnome/shell" = {
## dock icons
favorite-apps = [
"org.gnome.Nautilus.desktop"
"firefox.desktop"
"chromium-browser.desktop"
"spotify.desktop"
];
};
"org/gnome/shell/keybindings" = {
# disable original super+A
toggle-application-view = [];
};
"org/gnome/desktop/session" = {
idle-delay=lib.hm.gvariant.mkUint32 0; # never turn off screen
};
"org/gnome/settings-daemon/plugins/power" = {
ambient-enabled=false;
idle-dim=false;
power-button-action="nothing";
sleep-inactive-ac-timeout=3600;
sleep-inactive-ac-type="nothing";
sleep-inactive-battery-type="suspend";
};
# predefined keyboard shortcuts
"org/gnome/desktop/wm/keybindings" = {
switch-applications=[];
switch-applications-backward=[];
switch-windows=["<Alt>Tab"];
switch-windows-backward=["<Shift><Alt>Tab"];
maximize=["<Super>Up"];
unmaximize=["<Super>Down"];
minimize=[];
move-to-workspace-left=["<Control><Super>Left"];
move-to-workspace-right=["<Control><Super>Right"];
switch-input-source=["<Control>space"];
switch-input-source-backward=["<Shift><Control>space"];
};
# nautilus
"org/gtk/settings/file-chooser" = {
sort-directories-first=true;
};
"org/gnome/desktop/interface" = {
enable-hot-corners=false;
show-battery-percentage=true;
};
# proxy
"system/proxy" = {mode = "manual";};
"system/proxy/ftp" = {host="127.0.0.1"; port=config.proxyPort;};
"system/proxy/http" = {host="127.0.0.1"; port=config.proxyPort;};
"system/proxy/https" = {host="127.0.0.1"; port=config.proxyPort;};
"org/gnome/mutter" = {
dynamic-workspaces = true;
};
};
systemd.user.tmpfiles.rules = [
"L? %h/.config/gtk-3.0/bookmarks - - - - %h/Gist/Config/nautilus_bookmarks"
];
}
advanced-alttab-window-switcher
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.advanced-alttab-window-switcher
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"advanced-alt-tab@G-dH.github.com"
];
};
"org/gnome/shell/extensions/advanced-alt-tab-window-switcher" = {
switcher-popup-preview-selected=2;
win-switcher-popup-filter=2;
};
};
}
{ pkgs, ... }: {
home.packages = [(
pkgs.gnomeExtensions.auto-accent-colour.overrideAttrs (old: {
postPatch = ''
substituteInPlace extension.js --replace "gjs" "${pkgs.gjs}/bin/gjs"
'';
})
)];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"auto-accent-colour@Wartybix"
];
};
"org/gnome/shell/extensions/auto-accent-colour" = {
hide-indicator = true;
};
};
}
BingWallpaper
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.bing-wallpaper-changer
# random-wallpaper-wip-v3
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"BingWallpaper@ineffable-gmail.com"
# "randomwallpaper@iflow.space"
];
};
"org/gnome/shell/extensions/bingwallpaper" = {
market="zh-CN";
delete-previous=true;
download-folder="/tmp/pictures";
};
# "org/gnome/shell/extensions/space-iflow-randomwallpaper" = {
# auto-fetch = true;
# change-lock-screen = true;
# hours = 8;
# minutes = 29;
# source = "genericJSON";
# # source = "wallhaven";
# };
# "org/gnome/shell/extensions/space-iflow-randomwallpaper/genericJSON" = {
# generic-json-request-url = "http://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&mkt=zh-CN";
# generic-json-response-path = "$.images[0].url";
# generic-json-url-prefix = "http://www.bing.com";
# };
# "org/gnome/shell/extensions/space-iflow-randomwallpaper/wallhaven" ={
# wallhaven-keyword="cardcaptor sakura";
# };
};
}
bluetooth battery meter
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.bluetooth-battery-meter
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"Bluetooth-Battery-Meter@maniacx.github.com"
];
};
"org/gnome/shell/extensions/Bluetooth-Battery-Meter" = {
enable-battery-level-text = true;
};
};
}
customize-ibus: input method customization
{ pkgs, lib, ... }: {
home.packages = [
pkgs.gnomeExtensions.customize-ibus
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"customize-ibus@hollowman.ml"
];
};
"org/gnome/desktop/input-sources" = {
sources = with lib.hm.gvariant; mkArray
"(${lib.concatStrings [type.string type.string]})" [
(mkTuple ["xkb" "us"])
(mkTuple ["ibus" "rime"])
(mkTuple ["ibus" "mozc-jp"])
(mkTuple ["ibus" "hangul"])
];
};
"org/gnome/shell/extensions/customize-ibus" = {
candidate-orientation = lib.hm.gvariant.mkUint32 1;
custom-font="Iosevka Nerd Font 16";
enable-orientation=true;
input-indicator-only-on-toggle=false;
input-indicator-only-use-ascii=false;
use-custom-font=true;
use-indicator-show-delay=true;
};
};
}
dash-to-dock
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.dash-to-dock
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"dash-to-dock@micxgx.gmail.com"
];
};
"org/gnome/shell/extensions/dash-to-dock" = {
click-action="focus-or-appspread";
transparency-mode = "FIXED";
background-opacity = 0.4;
shortcut= [];
};
};
}
Gnome Extensions
Each time home-manager switched, no need log out.
Just reload extension by
{ pkgs, ... }: {
imports = [
./advanced-alttab-window-switcher.nix
./bing-wallpaper-changer.nix
./bluetooth-battery-meter.nix
./customize-ibus.nix
./dash-to-dock.nix
./gtile.nix
./hide-top-bar.nix
./pano.nix
./system-monitor.nix
./transparent-top-bar.nix
./unite.nix
./lunar-calendar.nix
./auto-accent-colour.nix
];
home.packages = with pkgs.gnomeExtensions; [
always-show-titles-in-overview
x11-gestures
] ++ [
pkgs.gnome45Extensions."permanent-notifications@bonzini.gnu.org"
];
dconf.settings = {
"org/gnome/shell" = {
disable-extension-version-validation = true;
## enabled gnome extensions
disable-user-extensions = false;
disabled-extensions = [];
enabled-extensions = [
"Always-Show-Titles-In-Overview@gmail.com"
"x11gestures@joseexposito.github.io"
"permanent-notifications@bonzini.gnu.org"
];
};
};
}
gtile: tiled windows manager
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.gtile
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"gTile@vibou"
];
};
"org/gnome/shell/extensions/gtile" = {
animation=true;
global-presets=true;
grid-sizes="16x10";
preset-resize-1=["<Super>bracketleft"];
preset-resize-2=["<Super>bracketright"];
preset-resize-3=["<Super>comma"];
preset-resize-4=["<Super>period"];
preset-resize-5=["<Super>semicolon"];
preset-resize-6=["<Super>apostrophe"];
preset-resize-7=["<Super>slash"];
resize1="2x2 1:1 1:1";
resize2="2x2 2:1 2:1";
resize3="2x2 1:2 1:2";
resize4="2x2 2:2 2:2";
resize5="4x8 2:2 3:7";
resize6="1x2 1:1 1:1";
resize7="1x2 1:2 1:2";
show-icon=false;
};
};
}
hide-top-bar
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.hide-top-bar
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"hidetopbar@mathieu.bidon.ca"
];
};
"org/gnome/shell/extensions/hidetopbar" = {
mouse-sensitive = true;
enable-active-window=false;
enable-intellihide=true;
# enable shortcut <Super>+v show nofication list, <Super>+s show quick settings, and so on
keep-round-corners=true;
shortcut-delay = 0.0;
shortcut-keybind = ["<Super>h"];
};
};
}
{ pkgs, ... }: {
home.packages = with pkgs.gnomeExtensions; [
lunar-calendar
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"lunarcal@ailin.nemui"
];
};
"org/gnome/shell/extensions/lunar-calendar" = {
# 日历内的节日
jrrilinei = true;
# 顶栏日期
show-date = true;
# 顶栏时间
show-time = false;
# 语言:大陆
yuyan = 0;
};
};
}
pano: Next-gen Clipboard Manager for Gnome Shell
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.pano
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"pano@elhan.io"
];
};
"org/gnome/shell/extensions/pano" = {
play-audio-on-copy=false;
send-notification-on-copy=false;
paste-on-select=false;
};
};
}
system-monitor
{ pkgs, ... }: {
home.packages = [
# replace system-monitor(-next) with vitals
# refers to https://github.com/mgalgs/gnome-shell-system-monitor-applet/issues/57
# vitals
pkgs.gnomeExtensions.system-monitor-next
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
# "Vitals@CoreCoding.com"
"system-monitor-next@paradoxxx.zero.gmail.com"
];
};
# "org/gnome/shell/extensions/vitals" = {
# fixed-widths = true;
# hide-icons = true;
# hot-sensors = [
# "_processor_usage_"
# "_memory_usage_"
# "__network-rx_max__"
# "__network-tx_max__"
# ];
# show-fan = false;
# show-system = false;
# show-temperature = false;
# show-voltage = false;
# update-time = 2;
# };
"org/gnome/shell/extensions/system-monitor-next-applet" = {
compact-display = true;
icon-display = false;
swap-display = true;
cpu-style = "digit";
memory-style = "digit";
net-style = "digit";
swap-style = "digit";
cpu-show-text = false;
memory-show-text = false;
net-show-text = false;
swap-show-text = false;
};
};
}
transparent-top-bar: able to adjust transparency
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.transparent-top-bar-adjustable-transparency
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [
"transparent-top-bar@ftpix.com"
];
};
"com/ftpix/transparentbar" = {
dark-full-screen = false;
transparency = 40;
};
};
}
unite: unite style
{ pkgs, ... }: {
home.packages = [
pkgs.gnomeExtensions.unite
];
dconf.settings = {
"org/gnome/shell" = {
enabled-extensions = [ "unite@hardpixel.eu" ];
};
"org/gnome/shell/extensions/unite" = {
app-menu-ellipsize-mode="end";
extend-left-box=false;
greyscale-tray-icons=false;
hide-app-menu-icon=false;
hide-dropdown-arrows=true;
hide-window-titlebars="always";
notifications-position="center";
reduce-panel-spacing=true;
show-window-buttons="always";
use-activities-text = false;
window-buttons-placement="last";
window-buttons-theme="materia";
restrict-to-primary-screen=false;
};
};
}
KDE Connect
{ config, pkgs, stdenv, lib, ... }:
let
mykdeconnect = pkgs.plasma5Packages.kdeconnect-kde;
This customized version of KDE Connect allow share PC's keyboard smoothly to android.
#mykdeconnect = pkgs.kdeconnect.overrideAttrs (old: {
# patches = [( pkgs.fetchpatch {
# url = "https://raw.githubusercontent.com/xieby1/kdeconnect-kde-enhanced/4610431b932b2fab05d7e0fc55e7306dc7ff0910/diff.patch";
# hash = "sha256-NL/TVOMEhdJ/W7UTxjF7Qjnq7JciNvl08BC1wrBfvHo=";
# })];
# # cmakeFlags = "-DCMAKE_BUILD_TYPE=Debug -DQT_FORCE_STDERR_LOGGING=1";
#});
in {
home.packages = [
mykdeconnect
];
Auto startup KDE Connect after Gnome GUI login.
home.file.kde_connect_indicator = {
source = "${mykdeconnect}/share/applications/org.kde.kdeconnect.nonplasma.desktop";
target = ".config/autostart/org.kde.kdeconnect.nonplasma.desktop";
};
}
kitty
{ config, pkgs, stdenv, lib, ... }:
{
# Terminal Comparsion
# * gnome-terminal
# * Pros
# * Cons
# * Wrong window size, when window is tiled (e.g. use gTile)
# * Icon display incorrectly (e.g. Vim-vista)
# * It's hard to hide top bar
# * alacritty
# * Pros
# * Esay configurable
# * Icon display correctly (e.g. Vim-vista)
# * input method fcitx works
# * Cons
# * Not native tab support
# https://github.com/alacritty/alacritty/issues/3129
# The developer(s?) with poor attitude
# * Compromise: tabbed
# * Alacritty is not work with espanso
# https://github.com/federico-terzi/espanso/issues/787
# https://github.com/federico-terzi/espanso/issues/1088
# In espanso auto mode, alacritty definitely will be stucked.
# In clipboard, alacritty may be stucked.
# * emoji display poorly.
# * kitty
# * Pros
# * Esay configurable
# * Cons
# * Icon display incorrectly (e.g. Vim-vista)
# * input method support solved by fcitx5
# * hyper
# * Cons
# * scroll speed is too fast!
imports = [
./timer.nix
./search.nix
];
# shortcuts
dconf.settings."org/gnome/settings-daemon/plugins/media-keys".custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/kitty/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/fzf-doc/"
];
dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/kitty" = {
binding="<Primary><Alt>t";
command = "kitty";
name="terminal";
};
dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/fzf-doc" = {
binding="<Super>f";
# bash alias needs interative bash (-i)
# https://askubuntu.com/questions/1109564/alias-not-working-inside-bash-shell-script
command="kitty bash -i fzf-doc";
name="fzf-doc";
};
programs.kitty = {
enable = true;
environment = {
"TERM" = "xterm";
};
font = {
name = "";
size = 16;
};
settings = {
allow_remote_control = "yes";
listen_on = "unix:/tmp/mykitty";
cursor_blink_interval = "0.8";
remember_window_size = "no";
initial_window_width = "80c";
initial_window_height = "20c";
# tab
tab_bar_edge = "top";
tab_bar_style = "separator";
active_tab_foreground = "#e2e2e3";
active_tab_background = "#2c2e34";
active_tab_title_template = "🐱{fmt.fg.red}{bell_symbol}{activity_symbol}{fmt.fg.tab}{title}";
inactive_tab_foreground = "#2c2e34";
inactive_tab_background = "#e2e2e3";
# tango dark
background = "#2c2e34";
foreground = "#e2e2e3";
cursor = "#e2e2e3";
color0 = "#2c2e34";
color8 = "#555753";
color1 = "#cc0000";
color9 = "#ef2929";
color2 = "#4e9a06";
color10 = "#8ae234";
color3 = "#c4a000";
color11 = "#fce94f";
color4 = "#3465a4";
color12 = "#729fcf";
color5 = "#75507b";
color13 = "#ad7fa8";
color6 = "#06989a";
color14 = "#34e2e2";
color7 = "#e2e2e3";
color15 = "#eeeeec";
# selection_foreground = "#2c2e34";
# selection_background = "#e2e2e3";
};
extraConfig = ''
map ctrl+equal change_font_size all +2.0
map ctrl+plus change_font_size all +2.0
map ctrl+kp_add change_font_size all +2.0
map ctrl+minus change_font_size all -2.0
map ctrl+kp_subtract change_font_size all -2.0
map ctrl+0 change_font_size all 0
map alt+1 goto_tab 1
map alt+2 goto_tab 2
map alt+3 goto_tab 3
map alt+4 goto_tab 4
map alt+5 goto_tab 5
map alt+6 goto_tab 6
map alt+7 goto_tab 7
map alt+8 goto_tab 8
map alt+9 goto_tab 9
map alt+0 goto_tab 99
map ctrl+shift+t new_tab_with_cwd
map ctrl+shift+n new_os_window_with_cwd
# disable opening of URLs with a plain click
mouse_map left click ungrabbed no_op
#: asks which tab to move the window into
map f2 detach_window ask
action_alias launch_window launch --cwd=current
# Window layout
enabled_layouts splits
# Split and Create a new window
map f5 launch_window --location=hsplit
map f6 launch_window --location=vsplit
# Goto window
map alt+left neighboring_window left
map alt+right neighboring_window right
map alt+up neighboring_window up
map alt+down neighboring_window down
'';
};
}
{ pkgs, ... }: let
kitty-kitten-search = pkgs.runCommand "kitty-kitten-search" {
src = pkgs.fetchFromGitHub {
owner = "trygveaa";
repo = "kitty-kitten-search";
rev = "0760138fad617c5e4159403cbfce8421ccdfe571";
hash = "sha256-egisza7V5dWplRYHIYt4bEQdqXa4E7UhibyWJAup8as=";
};
} ''
mkdir $out
cp $src/scroll_mark.py $out/scroll_mark.py
sed 's/typing/typing_compat/' $src/search.py > $out/search.py
'';
in {
home.file.kitty_search = {
source = "${kitty-kitten-search}/search.py";
target = ".config/kitty/search.py";
};
home.file.kitty_scroll_mark = {
source = "${kitty-kitten-search}/scroll_mark.py";
target = ".config/kitty/scroll_mark.py";
};
programs.kitty = {
extraConfig = ''
map ctrl+shift+f launch --location=hsplit --allow-remote-control kitty +kitten search.py @active-kitty-window-id
'';
};
}
{ lib, ... }: {
## after direnv's bash.initExtra
programs.bash.initExtra = lib.mkOrder 2000 ''
# https://stackoverflow.com/questions/1862510/how-can-the-last-commands-wall-time-be-put-in-the-bash-prompt
function timer_start {
_timer=''${_timer:-$SECONDS}
}
function timer_stop {
last_timer=$(($SECONDS - $_timer))
unset _timer
_notification="[''${last_timer}s⏰] Job finished!"
if [[ "$TERM" =~ tmux ]]; then
# https://github.com/tmux/tmux/issues/846
printf '\033Ptmux;\033\x1b]99;;%s\033\x1b\\\033\\' "$_notification"
else
printf '\x1b]99;;%s\x1b\\' "$_notification"
fi
}
'';
programs.kitty = {
extraConfig = ''
# Notify when a long running command is finished
## https://github.com/kovidgoyal/kitty/issues/1892
map f1 send_text all \x1a timer_start; fg; timer_stop \r
'';
};
}
# https://nixos.org/manual/nixos/stable/#sec-writing-modules
# refers to syncthing module
# list options:
# home-manager option xdg.mime.types.\"*\"
# nixos option
{ config
, lib
, pkgs
, ...
}:
with lib;
let
cfg = config.xdg.mime.types;
in
{
options = {
xdg.mime.types = mkOption {
default = {};
description = "Set MIME types and default applications.";
example = ''
xdg.mime.types.dot = {
name = "graphviz-dot";
type = "text/graphviz-dot";
pattern = "*.dot";
defaultApp = "xdot.desktop";
};
'';
type = types.attrsOf (types.submodule ({name, ...}:
{
options = {
name = mkOption {
type = types.str;
default = name;
description = "The name of the xml file.";
};
type = mkOption {
type = types.str;
default = "";
description = "The mime-type.";
};
pattern = mkOption {
type = types.str;
default = "";
description = "The glob pattern.";
};
defaultApp = mkOption {
type = types.str;
default = "";
description = "Default application for opening this MIME type.";
};
};
}));
};
};
# builtins.mapAttrs (n: v: {wang=v.miao;}) {file1={miao=1;}; file2={miao=2;};}
# => {file1={wang=1;}; file2={wang=2;};}
config = {
home.file = builtins.mapAttrs (n: v: {
text = ''
<?xml version="1.0"?>
<mime-info xmlns='http://www.freedesktop.org/standards/shared-mime-info'>
<mime-type type="${v.type}">
<glob pattern="${v.pattern}"/>
</mime-type>
</mime-info>
'';
target = ".local/share/mime-types/${v.name}.xml";
onChange = ''
${pkgs.xdg-utils}/bin/xdg-mime install ~/.local/share/mime-types/${v.name}.xml
${pkgs.xdg-utils}/bin/xdg-mime default ${v.defaultApp} ${v.type}
'';
}) cfg;
};
}
{ config, pkgs, stdenv, lib, ... }:
let
my-rofi = pkgs.rofi.override {
plugins = with pkgs; [
# rofi-file-browser
];
};
in
{
home.packages = with pkgs; [
my-rofi
];
# gnome keyboard shortcuts
dconf.settings."org/gnome/settings-daemon/plugins/media-keys".custom-keybindings = [
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_window/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_drun/"
"/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_wm/"
];
dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_window" = {
binding="<Super>w";
command="rofi -show window -window-format '{c} {t}'";
name="rofi window";
};
dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_drun" = {
binding="<Super>d";
command="rofi -show drun";
name="rofi drun";
};
dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_wm" = let
wm = pkgs.python3Packages.callPackage (pkgs.fetchFromGitHub {
owner = "xieby1";
repo = "rofi-wm";
rev = "5d3a13d3293b2e3a06a528f03bdfa99db5d4fd7b";
hash = "sha256-rWn4k2AD5vRv0Dz+K1aIOmG74Nilw5NZPs3xQCIS3Ew=";
}) {};
in {
binding="<Super>q";
command="rofi -show wm -modes wm:${wm}/bin/wm.py";
name="rofi wm";
};
home.file.rofi_config = {
target = ".config/rofi/config.rasi";
text = ''
/* This is a comment */
/* rofi -dump-config */
configuration {
modes: [
window,
drun,
run,
ssh
/* file-browser-extended */
];
terminal: "kitty";
dpi: 1;
show-icons: true;
}
filebrowser {
directory: "~/Documents";
}
/* man rofi-theme */
window {
width: 80%;
}
'';
};
# home.file.rofi_file_browser_config = let
# openDir = pkgs.writeScript "openDir" ''
# if [[ -d "$1" ]]; then
# xdg-open "$1"
# elif [[ -f "$1" ]]; then
# xdg-open "''${1%/*}"
# fi
# '';
# in {
# target = ".config/rofi/file-browser";
# text = ''
# # This is a comment
# dir ~/Documents
# depth 0
# no-sort-by-type
# sort-by-depth
# # BUG: rofi -show-icons causes segmentation fault
# # oc-search-path
# # oc-cmd "nautilus"
# # oc-cmd "${openDir}"
# '';
# };
}
rustdesk, the remote desktop app
{ config, pkgs, stdenv, lib, ... }:
{
home.packages = [
The new version of rustdesk is rustdesk-flutter, which is cached in official nix binary cache.
pkgs.rustdesk-flutter
];
Auto startup rustdesk after Gnome GUI login.
# home.file.autostart_rustdesk_desktop = {
# source = "${pkgs.rustdesk-flutter}/share/applications/rustdesk.desktop";
# target = ".config/autostart/rustdesk.desktop";
# };
}
{ config, pkgs, ... }:
# TODO: change it into a module
# TODO: split
let
singleton = pkgs.writeShellScriptBin "singleton.sh" ''
if [[ $# -lt 2 || $1 == "-h" ]]
then
echo "Usage: ''${0##*/} <window> <command and its args>"
echo " Only start a app once, if the app is running"
echo " then bring it to foreground"
exit 0
fi
if [[ "$1" == "kdeconnect.app" ]]
then
WID=$(${pkgs.xdotool}/bin/xdotool search --classname "$1")
else
WID=$(${pkgs.xdotool}/bin/xdotool search --onlyvisible --name "$1")
fi
if [[ -z $WID ]]
then
eval "''${@:2}"
else
for WIN in $WID
do
CURDESK=$(${pkgs.xdotool}/bin/xdotool get_desktop)
${pkgs.xdotool}/bin/xdotool set_desktop_for_window $WIN $CURDESK
${pkgs.xdotool}/bin/xdotool windowactivate $WIN
done
fi
'';
singleton_sh = "${singleton}/bin/singleton.sh";
open_my_cheatsheet_md_sh = pkgs.writeShellScript "open_my_cheatsheet_md" ''
cd ${config.home.homeDirectory}/Documents/Tech
kitty nvim my_cheatsheet.mkd -c Outline
make
'';
in
{
xdg.desktopEntries = {
# singleton apps
my_cheatsheet_md = {
name = "Cheatsheet Edit MD";
genericName = "cheatsheet";
exec = "${singleton_sh} my_cheatsheet.mkd ${open_my_cheatsheet_md_sh}";
icon = builtins.toFile "cheatsheet.svg" ''
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" rx="20%" ry="20%" fill="#666666"/>
<text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" font-size="18" font-weight="bold" fill="#F5F5F5">
<tspan x="50%" dy="-1.0em">CS</tspan>
<tspan x="50%" dy="1.0em">Edit</tspan>
<tspan x="50%" dy="1.0em">MD</tspan>
</text>
</svg>
'';
};
};
}
Typora
采用nixpkgs支持的最后的typora版本,即0.9.98。
mytypora = (pkgs.callPackage (pkgs.fetchurl {
url = "https://raw.githubusercontent.com/NixOS/nixpkgs/137f19d1d48b6d7c7901bb86729a2bce3588d4e9/pkgs/applications/editors/typora/default.nix";
sha256 = "057dk4hl4fljn50098g7l35sh7gwm7zqqqlrczv5lhmpgxi971c1";
}) {}).overrideAttrs (old: {
src = pkgs.fetchurl {
url = "https://web.archive.org/web/20211222112532/https://download.typora.io/linux/typora_0.9.98_amd64.deb";
sha256 = "1srj1fdcblfdsfvdnrqnwsxd3y8qd1h45p4sf1mxn6hr7z2s6ai6";
};
});
注:我尝试打包0.11.18, 发现这个版本会检测文件完整性, 因此基本上没办法用nix进行二次打包。
{ pkgs, lib, ... }: {
home.packages = [ pkgs.warpd ];
home.file.autostart_warpd = {
target = ".config/autostart/warpd.desktop";
text = ''
[Desktop Entry]
Type=Application
Name=warpd
Exec=${pkgs.warpd}/bin/warpd
'';
};
# 为什么不用gnome快捷键?
# * 一次性warpd启动比warpd后台运行慢
# 为什么用gnome快捷键
# * warpd没办法正确识别M-k键?或者是gnome抢了?要M-kk才行
# dconf.settings."org/gnome/settings-daemon/plugins/media-keys".custom-keybindings = [
# "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/warpd-hint/"
# "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/warpd-normal/"
# ];
# dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/warpd-hint" = {
# binding="<Super>k";
# command = "warpd --hint";
# name="warpd-hint";
# };
# dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/warpd-normal" = {
# binding="<Alt>k";
# command = "warpd --normal";
# name="warpd-normal";
# };
home.file.warpd_config = {
target = ".config/warpd/config";
# Available options see `warpd --list-options`.
# Available modifiers (according to warpd source code):
# * alt: A-
# * super/meta: M-
# * shift: S-
# * ctrl: C-
# Available X11 key names by removing "XK_" prefix in ${pkgs.xorg.xorgproto}/include/X11/keysymdef.h
text = lib.generators.toKeyValue {mkKeyValue=lib.generators.mkKeyValueDefault {} ": ";} {
activation_key = "A-M-z"; # 一只手按A-M-z比A-M-c更方便!
buttons = "q w e";
left = "Left";
up = "Up";
right = "Right";
down = "Down";
scroll_up = "S-Up";
scroll_down = "S-Down";
};
};
}
Wayland
virtualisation.waydroid.enable = true;
尝试使用weston,消息来源参考:
- https://github.com/waydroid/waydroid/issues/470
- https://wiki.archlinux.org/title/Waydroid
wayland images: /var/lib/waydroid/images/
weston --scale=2
# 在weston里
waydroid show-full-ui
weston快捷键参考man weston-bindings
{ pkgs ? import <nixpkgs> {}
, wrapWine ? import ./wrapWine.nix {inherit pkgs;}
}:
let
name = "weixin";
installer = builtins.fetchurl "https://dldir1.qq.com/weixin/Windows/WeChatSetup.exe";
regfile = builtins.toFile "${name}.reg" ''
Windows Registry Editor Version 5.00
# [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Hardware Profiles\Current\Software\Fonts]
# "LogPixels"=dword:000000F0
[HKEY_CURRENT_USER\Software\Wine\X11 Driver]
"Decorated"="N"
'';
bin = wrapWine {
inherit name;
executable = "$WINEPREFIX/drive_c/Program Files/Tencent/WeChat/WeChat.exe";
tricks = ["riched20" "msls31"];
setupScript = ''
LANG="zh_CN.UTF-8"
'';
firstrunScript = ''
# prevent weixin polluting my Documents folder
rm -f $WINEPREFIX/drive_c/users/$USER/Documents
mkdir -p $WINEPREFIX/drive_c/users/$USER/Documents
wine ${installer}
# 占用磁盘空间持续增加
# https://github.com/vufa/deepin-wine-wechat-arch/issues/225
mkdir -p $WINEPREFIX/drive_c/users/xieby1/AppData/Roaming/Tencent/WeChat/xweb/crash/Crashpad/
touch $WINEPREFIX/drive_c/users/xieby1/AppData/Roaming/Tencent/WeChat/xweb/crash/Crashpad/reports
'';
inherit regfile;
};
desktop = pkgs.makeDesktopItem {
inherit name;
desktopName = "Wine微信";
genericName = "weixin";
type = "Application";
exec = "${bin}/bin/${name}";
icon = pkgs.fetchurl {
url = "https://cdn.cdnlogo.com/logos/w/79/wechat.svg";
sha256 = "1xk1dsia6favc3p1rnmcncasjqb1ji4vkmlajgbks0i3xf60lskw";
};
};
in
pkgs.symlinkJoin {
inherit name;
paths = [bin desktop];
}
# based on https://github.com/lucasew/nixcfg/blob/49d44c1a655f1c20d7354ecea942c78704067d50/pkgs/wrapWine.nix
{ pkgs ? import <nixpkgs> {} }:
let
inherit (builtins) length concatStringsSep;
inherit (pkgs) lib cabextract
writeShellScriptBin symlinkJoin;
inherit (lib) makeBinPath;
in
{ is64bits ? false
, wine ? if is64bits then pkgs.wineWowPackages.stable else pkgs.wine
, wineFlags ? ""
, executable
, chdir ? null
, name
, tricks ? [ ]
, setupScript ? ""
, firstrunScript ? ""
, home ? ""
, regfile ? null
}:
let
wineBin = "${wine}/bin/wine${if is64bits then "64" else ""}";
requiredPackages = [
wine
cabextract
];
PATH = makeBinPath requiredPackages;
NAME = name;
WINEARCH =
if is64bits
then "win64"
else "win32";
WINE_NIX = "$HOME/.wine-nix";
WINEPREFIX = "${WINE_NIX}/${name}";
setupHook = ''
${wine}/bin/wineboot
'';
tricksHook =
if (length tricks) > 0 then
let
tricksStr = concatStringsSep " " tricks;
tricksCmd = ''
${pkgs.winetricks}/bin/winetricks ${tricksStr}
'';
in
tricksCmd
else "";
run = writeShellScriptBin name ''
export APP_NAME="${NAME}"
export WINEARCH=${WINEARCH}
export WINE_NIX=${WINE_NIX}
export PATH=$PATH:${PATH}
export WINEPREFIX="${WINEPREFIX}"
export EXECUTABLE="${executable}"
mkdir -p "$WINE_NIX"
${setupScript}
if [ ! -e "$EXECUTABLE" ] # if the executable does not exist
then
${if regfile!=null
then ''${wineBin} regedit /C ${regfile}''
else ""
}
${setupHook}
${tricksHook}
${firstrunScript}
else # no automatically run after installation!
${if chdir != null
then ''cd "${chdir}"''
else ""}
if [ ! "$REPL" == "" ]; # if $REPL is setup then start a shell in the context
then
bash
exit 0
fi
${wineBin} ${wineFlags} "$EXECUTABLE" "$@"
fi
'';
clean = writeShellScriptBin "${name}-clean" ''
read -p "Are you sure you want to clean ${WINEPREFIX}? <y/N> " prompt
if [[ $prompt =~ [yY](es)* ]]; then
rm ${WINEPREFIX} -rf
fi
'';
winecfg = writeShellScriptBin "${name}-cfg" ''
export WINEARCH=${WINEARCH}
WINEPREFIX=${WINE_NIX}/${name} winecfg $@
'';
_wine = writeShellScriptBin "${name}-wine" ''
export WINEARCH=${WINEARCH}
WINEPREFIX=${WINE_NIX}/${name} ${wineBin} $@
'';
in
symlinkJoin {
inherit name;
paths = [run clean winecfg _wine];
}
{ config, lib, pkgs, ... }: {
config = lib.mkIf config.isWSL2 {
fonts.fontconfig.enable = true;
home.packages = with pkgs; [
noto-fonts-cjk-sans
noto-fonts-cjk-serif
noto-fonts-emoji
];
};
}
XColor
Why use XColor instead of gcolor3 or eyedropper? Because they do not support zoom: e.g. https://github.com/FineFindus/eyedropper/issues/79
{ pkgs, ... }: {
home.packages = [(
let
name = "xcolor-sleep";
# [Error when launching xcolor from the Activities overview: Could not grab pointer #38](https://github.com/Soft/xcolor/issues/38)
exec = pkgs.writeShellScript name ''
for ((n=0; n<3; n++)); do
${pkgs.xcolor}/bin/xcolor -s && break
sleep 0.2
done
'';
in
pkgs.runCommand name {} ''
mkdir -p $out
${pkgs.xorg.lndir}/bin/lndir -silent ${pkgs.xcolor} $out
rm $out/share/applications/XColor.desktop
sed 's,^Exec.*,Exec=${exec},' ${pkgs.xcolor}/share/applications/XColor.desktop > $out/share/applications/XColor.desktop
''
)];
}
xdot, the dot (graphviz) viewer
{ config, pkgs, stdenv, lib, ... }:
let
Add a xdot.desktop for my xdot.
myxdot = pkgs.symlinkJoin {
name = "myxdot";
paths = [
pkgs.xdot
(pkgs.makeDesktopItem {
name = "xdot";
desktopName = "xdot";
exec = "xdot %U";
icon = builtins.toFile "cheatsheet.svg" ''
<svg width="64" height="64" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" rx="20%" ry="20%" fill="#F5F5F5"/>
<text x="50%" y="50%" text-anchor="middle" dominant-baseline="middle" font-size="18" font-weight="bold" fill="#333333">
xdot
</text>
</svg>
'';
})];};
in {
home.packages = [
myxdot
];
Open *.dot files with xdot.desktop by default.
xdg.mime.types.dot = {
name = "graphviz-dot";
type = "text/graphviz-dot";
pattern = "*.dot";
defaultApp = "xdot.desktop";
};
}
{
lib,
stdenv,
fetchurl,
dpkg,
qt5
}:
let
version = "0.05";
rpath = lib.makeLibraryPath [
qt5.qtbase
] + ":${stdenv.cc.cc.lib}/lib64";
src =
if stdenv.hostPlatform.system == "x86_64-linux" then
fetchurl {
urls = [
"https://github.com/horsicq/XELFViewer/releases/download/0.05/xelfviewer_0.05_Ubuntu_22.04_amd64.deb"
];
sha256 = "0l6j0pnpfzwr8205xzis95k4x2la0mfy08bv6hfg32rh3bw906bz";
}
else
throw "xelfviewer is not supported on ${stdenv.hostPlatform.system}";
in
stdenv.mkDerivation {
pname = "xelfviewer";
inherit version;
system = "x86_64-linux";
inherit src;
nativeBuildInputs = [ qt5.wrapQtAppsHook ];
buildInputs = [ dpkg ];
dontUnpack = true;
installPhase = ''
mkdir -p $out
dpkg-deb -x $src $out
mv $out/usr/* $out/
rm -r $out/usr
'';
postFixup = ''
for file in $(find $out -type f \( -perm /0111 -o -name \*.so\* \) ); do
patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" "$file" || true
patchelf --set-rpath ${rpath}:$out/lib/xelfviewer $file || true
done
'';
meta = with lib; {
description = "XELFViewer";
homepage = "https://github.com/horsicq/XELFViewer";
license = licenses.mit;
maintainers = with maintainers; [ xieby1 ];
platforms = [ "x86_64-linux" ];
};
}
{ config, lib, ...}:
{
imports = [../../modules/cachix.nix];
config = lib.mkIf (
(builtins.pathExists config.cachix_dhall) &&
(config.cachix_packages != [])
) {
home.activation.cachix_push = lib.hm.dag.entryAfter ["writeBoundary"] config._cachix_push;
};
}
nix-on-droid.nix
nix-on-droid.nix
是安卓nix-on-droid的入口配置文件。
因为原生termux并不能直接运行nix,
所以nix-on-droid的作者基于termux使用proot搞出来一个能运行nix的termux分支。
nix-on-droid并未自带很多常用linux命令行工具,
因此nix-on-droid.nix
主要负责配置那些在linux常用的,但nix-on-droid没有默认提供的命令行工具。
其他命令行工具的配置则复用home-manager的配置./usr
。
下面是我的带注解的nix-on-droid.nix
代码。
{ pkgs, config, ... }:
{
添加sshd-start
命令,用于在安卓上启动sshd服务。
通常安卓上是没有root权限的,无法写/etc/目录的文件,
因此将ssh的tmp目录和配置目录设定到home下面。
这部分内容参考nix-on-droid wiki: SSH access
imports = [(let
sshdTmpDirectory = "${config.user.home}/sshd-tmp";
sshdDirectory = "${config.user.home}/sshd";
pathToPubKey = "${config.user.home}/.ssh/id_rsa.pub";
port = 8022;
sshd-start = pkgs.writeScriptBin "sshd-start" ''
#!${pkgs.runtimeShell}
echo "Starting sshd in non-daemonized way on port ${toString port}"
${pkgs.openssh}/bin/sshd -f "${sshdDirectory}/sshd_config" -D
'';
in {
environment.packages = with pkgs; [
sshd-start
];
build.activation.sshd = ''
$DRY_RUN_CMD mkdir $VERBOSE_ARG --parents "${config.user.home}/.ssh"
$DRY_RUN_CMD cat ${pathToPubKey} > "${config.user.home}/.ssh/authorized_keys"
if [[ ! -d "${sshdDirectory}" ]]; then
$DRY_RUN_CMD rm $VERBOSE_ARG --recursive --force "${sshdTmpDirectory}"
$DRY_RUN_CMD mkdir $VERBOSE_ARG --parents "${sshdTmpDirectory}"
$VERBOSE_ECHO "Generating host keys..."
$DRY_RUN_CMD ${pkgs.openssh}/bin/ssh-keygen -t rsa -b 4096 -f "${sshdTmpDirectory}/ssh_host_rsa_key" -N ""
$VERBOSE_ECHO "Writing sshd_config..."
$DRY_RUN_CMD echo -e "HostKey ${sshdDirectory}/ssh_host_rsa_key\nPort ${toString port}\n" > "${sshdTmpDirectory}/sshd_config"
$DRY_RUN_CMD mv $VERBOSE_ARG "${sshdTmpDirectory}" "${sshdDirectory}"
fi
'';
}) ({
自动配置termux。
build.activation.termux = ''
DIR=${config.user.home}/.termux
mkdir -p $DIR
symlink() {
if [[ -e $1 && ! -e $2 ]]; then
#echo "ln -s $1 $2"
ln -s $1 $2
fi
}
SRC=${config.user.home}/Gist/Config/termux.properties
DST=$DIR/termux.properties
symlink $SRC $DST
SRC=${config.user.home}/Gist/Config/colors.properties
DST=$DIR/colors.properties
symlink $SRC $DST
'';
})];
下面是非常直观的软件安装。
# Simply install just the packages
environment.packages = with pkgs; [
# User-facing stuff that you really really want to have
vim # or some other editor, e.g. nano or neovim
# Some common stuff that people expect to have
diffutils
findutils
utillinux
tzdata
hostname
man
gnugrep
gnupg
gnused
gnutar
bzip2
gzip
xz
zip
unzip
gawk
openssh
nettools
(lib.setPrio # make bintools less prior
(busybox.meta.priority + 10)
busybox
)
];
# Backup etc files instead of failing to activate generation if a file already exists in /etc
environment.etcBackupExtension = ".bak";
# Read the changelog before changing this value
system.stateVersion = "21.11";
导入home-manager的配置文件./usr的配置。 如此操作,所有home-manager的软件和配置都能nix-on-droid中复用。
home-manager.config = import ./usr;
}
用于自动push包到cachix的模块。 尽管cachix watch-store能自动push,但是我想更细粒度地管理需要push的包,所以有了这个模块。
{ config, pkgs, lib, ...}:
{
options = {
cachix_packages = lib.mkOption {
type = lib.types.listOf lib.types.package;
default = [];
description = ''
This list of packages.
If the the cachix.dhall file exists and cachix_packages is not empty,
then the packages in cachix_packages will be pushed to cachix.
'';
};
cachix_dhall = lib.mkOption {
type = lib.types.str;
default = "/home/xieby1/Gist/Config/cachix.dhall";
description = ''
The path of cachix.dhall.
'';
};
cachix_name = lib.mkOption {
type = lib.types.str;
default = "xieby1";
description = ''
The cachix name.
'';
};
_cachix_push = lib.mkOption {
type = lib.types.str;
default = ''
mkdir -p ~/.config/cachix_push
${lib.concatMapStrings (pkg: ''
symlink=~/.config/cachix_push/${pkg.name}
if [[ ! (-h "$symlink" && $(realpath "$symlink")==${pkg}) ]]; then
# the symlink are not the same to pkg, need to cachix push
echo 📦 ${pkg}
${pkgs.cachix}/bin/cachix -c ${config.cachix_dhall} push ${config.cachix_name} ${pkg}
rm -f "$symlink"
ln -s ${pkg} "$symlink"
fi
'') config.cachix_packages}
'';
description = ''
(Internal usage) The script of pushing packages to cachix.
'';
};
};
}
{ lib, ... }: {
options = {
isMinimalConfig = lib.mkOption {
type = lib.types.bool;
default = false;
};
proxyPort = lib.mkOption {
type = lib.types.number;
default = 8889;
description = "代理端口号,诸多网络程序需要用,比如clash和tailscale";
};
isCli = lib.mkOption {
type = lib.types.bool;
default = (builtins.getEnv "DISPLAY")=="";
description = ''
`isCli`和`isGui`的默认值通过环境变量`DISPLAY`来判断是否是CLI或GUI环境。
这个方法有**局限**,比如ssh连接到一台有GUI的电脑上,ssh里是没有设置环境变量`DISPLAY`的。
因此更好的方法是显示地声明sCli和isGui的值。
'';
};
isGui = lib.mkOption {
type = lib.types.bool;
default = (builtins.getEnv "DISPLAY")!="";
};
isNixOnDroid = lib.mkOption {
type = lib.types.bool;
default = (builtins.getEnv "USER")=="nix-on-droid";
description = ''
默认值是通过用户名来判断是否是nix-on-droid。
'';
};
isWSL2 = lib.mkOption {
type = lib.types.bool;
default = (builtins.getEnv "WSL_DISTRO_NAME")!="";
description = ''
默认值是通过环境变量`WSL_DISTRO_NAME`来判断是否是WSL2。
'';
};
};
}
Module
nixpkgs/lib/modules.nix:
从loadModule
可以看出,imports可以接受Function、Attr、路径,不能够嵌套List。
Nixpkgs
nixpkgs crossSystem
- default.nix
- pkgs/top-level/impure.nix
- pkgs/top-level/default.nix
- lib.systems.elaborate crossSystem0;
- lib/systems/default.nix: elaborate
- parsed = parse.mkSystemFromString ... args.system; // args.system = crossSystem
- lib/systems/parse.nix
- mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (lib.splitString "-" s));
- mkSkeletonFromList
- mkSystemFromString = s: mkSystemFromSkeleton (mkSkeletonFromList (lib.splitString "-" s));
- lib/systems/parse.nix
- parsed = parse.mkSystemFromString ... args.system; // args.system = crossSystem
- lib/systems/default.nix: elaborate
- lib.systems.elaborate crossSystem0;
- pkgs/top-level/default.nix
- pkgs/top-level/impure.nix
注意mkSkeletonFromList通过
${toString (length l)}
实现了一个根据l长度的switch case。
crossSystem的4个域分别为cpu-vendor-kernel-abi
import nixpkgs
import <nixpgks> {}
的加载流程
- default.nix
- pkgs/top-level/impure.nix
- pkgs/top-level/default.nix
- stdenvStages import pkgs/stdenv/default.nix
- stagesLinux = import pkgs/stdenv/linux/
- thisStdenv, stdenv = import pkgs/stdenv/generic/default.nix
- import lib/default.nix
this is where customisation.nix come from
- import lib/default.nix
- thisStdenv, stdenv = import pkgs/stdenv/generic/default.nix
- stagesLinux = import pkgs/stdenv/linux/
- allPackages import pkgs/top-level/stage.nix
- allPackages import pkgs/top-level/all-packages.nix
- boot = import pkgs/stdenv/booter.nix
- stdenvStages import pkgs/stdenv/default.nix
- pkgs/top-level/default.nix
- pkgs/top-level/impure.nix
vim_configurable.override {python = python3} vimUtils.makeCustomizable (vim.override {python = python3}) vimUtils.makeCustomizable (vim.override {python = python3})
似乎是一个浮现override变量的最小集 f = a:{res = a+1;} fo = pkgsMiao.makeOverride f f 2
打包/编译32位程序
参考Packaging/32bit Applications。
仅32位
打包使用pkgs.pkgsi686Linux.stdenv.mkDerivation
编译使用pkgs.pkgsi686Linux.gcc
32位且64位
打包使用pkgs.multiStdenv.mkDerivation。
编译使用pkgs.gcc_multi。
Auto Push Packages to Cachix
Objective:
Automatically push specific built packages to cachix during home-manager switch
/nixos-rebuild switch
/nix-shell
.
Although there are several existing ways to achieve this:
- cachix's watch-exec and watch-store
- nix-build's post-build-hook
However, the granularity of these methods is coarse; they push all packages to cachix. Is there a way to allow users to control which packages are pushed?
Conclusion First
Use hooks to push the selected packages to cachix:
Scenario | Hook | Example |
---|---|---|
home-manager | home.activation | modules/cachix.nix, usr/modules/cachix.nix |
nixos-rebuild | system.activationScripts | modules/cachix.nix, sys/modules/cachix.nix |
nix-shell | shellHook | openc910/shell.nix |
My Explorations
Possible solutions:
Add a wrapper called cachixPackages
, which recives the packages to be pushed and cachix information.
This cachixPackages
is a dummy package whose build stages will push the packages to cachix.
However, normal nix packages are not allowed network access during building.
To tackle this, like how fetch* series functions are implemented, the fixed-output derivation can be utilized to allow network access.
However, the above method seems not work as below, because cachix needs accesses to some resources beyond nix-build process (such as nix-build's sandbox).
nix-build test.nix
...
cachix: CppStdException e "\ESC[31;1merror:\ESC[0m creating directory '\ESC[35;1m/nix/var\ESC[0m': \ESC[35;1mPermission denied\ESC[0m"(Just "nix::SysError")
result 1
...
Even though I disable the nix-build sandbox by using --no-sandbox
,
the cachix still does not satisfy as below.
$ nix-build test.nix --no-sandbox
...
cachix: CppStdException e "\ESC[31;1merror:\ESC[0m cannot open connection to remote store '\ESC[35;1mdaemon\ESC[0m': \ESC[35;1m\ESC[31;1merror:\ESC[0m reading from file: \ESC[35;1mConnection reset by peer\ESC[0m\ESC[0m"(Just "nix::Error")
...
If you curious about my demo of cachixPackages
and its test,
see cachix-package.nix and test.nix.
cachixPackage
This file try to implement a package wrapper, which will automatically push pkg
to cachix upon building.
However, this method seems not work, due to limited resources in nix-build environment.
For more details, see here.
{ cachix
, stdenv
, writeShellScript
}:
{ pkg
, sha256
, cachix_dhall
, cachix_name
, name ? "cachixed"
}:
builtins.derivation {
inherit name;
system = builtins.currentSystem;
builder = writeShellScript "cachix-package-builder" ''
source ${stdenv}/setup
echo ${pkg} > $out
if [[ -f "${cachix_dhall}" ]]; then
${cachix}/bin/cachix -c ${cachix_dhall} push ${cachix_name} ${pkg}
result=$?
echo result $result
exit $result
fi
'';
outputHashMode = "flat";
outputHashAlgo = "sha256";
outputHash = sha256;
}
Test for cachixPackage
To run the test:
# run with nix-build sandbox
nix-build test.nix
# run without nix-build sandbox
nix-build test.nix --no-sandbox
let
pkgs = import <nixpkgs> {};
cachixPackage = import ./cachix-package.nix {inherit (pkgs) cachix stdenv writeShellScript;};
in cachixPackage {
pkg = pkgs.hello;
sha256 = "01vm275n169r0ly8ywgq0shgk8lrzg79d1aarshwybwxwffj4q0q";
cachix_dhall = /home/xieby1/Gist/Config/cachix.dhall;
cachix_name = "xieby1";
}
备份binary cache
场景
官方binary cache没有的包, 自己花了很长时间编译出来。 值得把编译出来的的包及其依赖全部保存下来。
目前的问题
nix copy --to
交叉编译和安装跨平台程序
nixpkgs原生支持x86和arm指令集。 通过对nixpkgs配置可以轻松实现交叉编译, 跨平台程序的安装等功能。
太长不看
-
x86_64上的aarch64交叉编译器
(with import <nixpkgs> {crossSystem="aarch64-linux";}; stdenv.cc)
-
aarch64的hello应用程序
(with import <nixpkgs> {localSystem.system="aarch64-linux";crossSystem="aarch64-linux";}; hello)
-
应用于nix-shell的例子
目录
简介
nixpkgs1众多输入参数中,包含localSystem
和crossSystem
2。
-
localSystem
The system packages will be built on.
本地系统,即工具链运行的平台。
-
crossSystem
The system packages will ultimately be run on.
程序运行的平台。
通过localSystem
和crossSystem
不同值的组合,
可以实现交叉编译、安装其他架构的原生应用。
下面从localSystem
和crossSystem
的语法和应用两方面进行介绍。
语法章节从nixpkgs源码的角度出发,介绍其语法的组成。
应用章节围绕一个nix-shell脚本的实际例子,
介绍x86_64平台的交叉编译和安装aarch64架构的原生应用的方法。
localSystem
和crossSystem
的语法
localSystem
和crossSystem
由4个维度去刻画一个系统:cpu, vendor, kernel, abi。
localSystem
和crossSystem
的值为字符串或者{system=字符串;}
3。
system字符串为可以包含前述4个维度的1~4个维度。
nix在解析时会将省略的维度按以某些默认值补充完整。
维度之间之间用-
分割。
因此system字符串形式上为"cpu-vendor-kernel-abi"
。
字符串不同数量的维度及其可用的值,
按匹配优先级由高到低列举如下4,
cpu-vendor-kernel-abi
system字符串 | cpu | vendor | kernel | abi |
---|---|---|---|---|
"avr" | avr | none | unknown | |
"{cpu}-cygwin" | {cpu} | windows | cygnus | |
"{cpu}-windows" | {cpu} | windows | msvc | |
"{cpu}-elf" | {cpu} | unknown | none | elf |
"{cpu}-{kernel}" | {cpu} | {kernel} | ||
"{cpu}-apple-{kernel}" | {cpu} | apple | {kernel} | |
"{cpu}-linux-gnu" | {cpu} | linux | gnu | |
"{cpu}-{vendor}-mingw32" | {cpu} | {vendor} | windows | |
"{cpu}-{vendor}-wasi" | {cpu} | {vendor} | wasi | |
"{cpu}-{vendor}-redox" | {cpu} | {vendor} | redox | |
"{cpu}-{vendor}-mmixware" | {cpu} | {vendor} | mmixware | |
"{cpu}-{vendor}-netbsd*" | {cpu} | {vendor} | netbsd* | |
"{cpu}-{vendor}-eabi" | {cpu} | unknown | {kernel} | eabi |
"{cpu}-{vendor}-eabihf" | {cpu} | unknown | {kernel} | eabihf |
"{cpu}-{kernel}-elf" | {cpu} | unknown | {kernel} | elf |
"{cpu}-*-{ghcjs}" | {cpu} | unknown | ghcjs | |
"{cpu}-{vendor}-genode" | {cpu} | {vendor} | genode | |
"{cpu}-{vendor}-{kernel}-{abi} " | {cpu} | {vendor} | {kernel} | {abi} |
cpu
cpu字符串可取的值列举如下5,
cpu字符串 | bits | significantByte | family | version | arch |
---|---|---|---|---|---|
"arm" | 32 | littleEndian | "arm" | ||
"armv5tel" | 32 | littleEndian | "arm" | "5" | "armv5t" |
"armv6m" | 32 | littleEndian | "arm" | "6" | "armv6-m" |
"armv6l" | 32 | littleEndian | "arm" | "6" | "armv6" |
"armv7a" | 32 | littleEndian | "arm" | "7" | "armv7-a" |
"armv7r" | 32 | littleEndian | "arm" | "7" | "armv7-r" |
"armv7m" | 32 | littleEndian | "arm" | "7" | "armv7-m" |
"armv7l" | 32 | littleEndian | "arm" | "7" | "armv7" |
"armv8a" | 32 | littleEndian | "arm" | "8" | "armv8-a" |
"armv8r" | 32 | littleEndian | "arm" | "8" | "armv8-a" |
"armv8m" | 32 | littleEndian | "arm" | "8" | "armv8-m" |
"aarch64" | 64 | littleEndian | "arm" | "8" | "armv8-a" |
"aarch64_be" | 64 | bigEndian | "arm" | "8" | "armv8-a" |
"i386" | 32 | littleEndian | "x86" | "i386" | |
"i486" | 32 | littleEndian | "x86" | "i486" | |
"i586" | 32 | littleEndian | "x86" | "i586" | |
"i686" | 32 | littleEndian | "x86" | "i686" | |
"x86_64" | 64 | littleEndian | "x86" | "x86-64" | |
"mips" | 32 | bigEndian | "mips" | ||
"mipsel" | 32 | littleEndian | "mips" | ||
"mips64" | 64 | bigEndian | "mips" | ||
"mips64el" | 64 | littleEndian | "mips" | ||
"mmix" | 64 | bigEndian | "mmix" | ||
"m68k" | 32 | bigEndian | "m68k" | ||
"powerpc" | 32 | bigEndian | "power" | ||
"powerpc64" | 64 | bigEndian | "power" | ||
"powerpc64le" | 64 | littleEndian | "power" | ||
"powerpcle" | 32 | littleEndian | "power" | ||
"riscv32" | 32 | littleEndian | "riscv" | ||
"riscv64" | 64 | littleEndian | "riscv" | ||
"s390" | 32 | bigEndian | "s390" | ||
"s390x" | 64 | bigEndian | "s390" | ||
"sparc" | 32 | bigEndian | "sparc" | ||
"sparc64" | 64 | bigEndian | "sparc" | ||
"wasm32" | 32 | littleEndian | "wasm" | ||
"wasm64" | 64 | littleEndian | "wasm" | ||
"alpha" | 64 | littleEndian | "alpha" | ||
"msp430" | 16 | littleEndian | "msp430" | ||
"avr" | 8 | "avr" | |||
"vc4" | 32 | littleEndian | "vc4" | ||
"or1k" | 32 | bigEndian | "or1k" | ||
"js" | 32 | littleEndian | "js" |
cpuTypes
cpu之间的兼容性(具有传递性和自反性)如下6,
vendor
vendor字符串可取值"apple"
, "pc"
(windows), "w64"
(MinGW-w64), "none"
, "unknown"
(default)。
kernel
kernel字符串可取值如下表7,
kernel字符串 | execFormat | families |
---|---|---|
"macos" | macho | darwin |
"darwin" | ↑ | ↑ |
"ios" | macho | darwin |
"watchos" | ↑ | ↑ |
"tvos" | ↑ | ↑ |
"freebsd" | elf | bsd |
"linux" | elf | |
"netbsd" | elf | bsd |
"none" | unknown | |
"openbsd" | elf | bsd |
"solaris" | elf | |
"wasi" | wasm | |
"redox" | elf | |
"windows" | pe | |
"win32" | ↑ | ↑ |
"ghcjs" | unknown | |
"genode" | elf | |
"mmixware" | unknown |
abi
abi字符串可取的值列举如下8,
abi字符串 | float | abi | Note |
---|---|---|---|
"cygnus" | |||
"msvc" | |||
"eabi" | soft | for ARM, PowerPC | |
"eabihf" | hard | for ARM, PowerPC | |
"elf" | |||
"androideabi" | |||
"android" | not 32-bit | ||
"gnueabi" | soft | ||
"gnueabihf" | hard | ||
"gnu" | not 32-bit | ||
"gnuabi64" | 64 | ||
"musleabi" | soft | ||
"musleabihf" | hard | ||
"musl" | |||
"uclibceabihf" | soft | ||
"uclibceabi" | hard | ||
"uclibc" | |||
"unknown" |
localSystem
和crossSystem
的应用
aarch64交叉工具链和程序的详细例子
以x86为本地指令集,localSystem
和crossSystem
的组合有以下效果
↓crossSystem ↓ →localSystem → | "x86_64-linux" | "aarch64-linux" |
---|---|---|
"x86_64-linux" | 通常情况 | |
"aarch64-linux" | 交叉编译aarch64 | 原生aarch64应用 |
因此基于这3种组合,可以在同一个shell环境中配置出3种软件, 代码见[cross_platform.nix]({{ site.repo_url }}/scripts/shell/cross_platform.nix)。
pkgs_arm_cross
软件包的stdenv.cc
为x86平台的arm交叉编译器。
nixpkgs channel只包含了原生x86应用和原生arm应用。
交叉编译的arm应用和原生arm应用的derivation不一样。
因此使用pkgs_arm_cross
中的应用,
则会使用交叉编译器从源码开始编译arm应用,
而不是直接拉取nixpkgs channel的原生arm应用。
pkgs_arm_native
软件包包含原生arm软件包。
从这个软件包拉取的应用和在arm平台的拉取到的应用一致。
例如figlet
将直接从nix channel中拉取。
pkgs
即x86原生的软件包。
shell_cross_platform.nix使用例子,
# 创建一个新的shell环境,包含stdenv.cc, figlet, qemu
$ nix-shell shell_cross_platform.nix
# 使用交叉编译工具链的c编译器
$ aarch64-unknown-linux-gnu-gcc helloworld.c -o helloworld
$ file helloworld
helloworld: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /nix/store/01kw0gb38phviarfv3fca49dpqh0qwlx-glibc-aarch64-unknown-linux-gnu-2.33-123/lib/ld-linux-aarch64.so.1, for GNU/Linux 2.6.32, with debug_info, not stripped
# arm原生应用
$ file `command -v figlet`
/nix/store/4f70f04bvd664n00jlnzccyzxd35lykw-figlet-2.2.5/bin/figlet: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /nix/store/rjc27shzir243n1w3127w713fijamf6v-glibc-2.33-123/lib/ld-linux-aarch64.so.1, for GNU/Linux 2.6.32, not stripped
# 直接执行figlet会出错
$ figlet
bash: /nix/store/4f70f04bvd664n00jlnzccyzxd35lykw-figlet-2.2.5/bin/figlet: cannot execute binary file: Exec format error
# 使用QEMU执行figlet即可
$ qemu-aarch64 `command -v figlet` miao!
_ _
_ __ ___ (_) __ _ ___ | |
| '_ ` _ \| |/ _` |/ _ \| |
| | | | | | | (_| | (_) |_|
|_| |_| |_|_|\__,_|\___/(_)
mips交叉工具链的例子
[cross_mips.nix]({{ site.repo_url }}/scripts/shell/cross_mips.nix)
crossSystem的传递
- pkgs/top-level/stage.nix:
-
pkgsCross = lib.mapAttrs (n: crossSystem: nixpkgsFun { inherit crossSystem; }) lib.systems.examples;
-
- 其中pkgs/top-level/default.nix:
nixpkgsFun = newArgs: import ./. (args // newArgs);
- 也就是说lib.systems.examples的每个元素作为crossSystem传递给了pkgs/top-level/default.nix
- pkgs/top-level/default.nix:
- 首先crossSystem = lib.systems.elaborate crossSystem0
- 接下来是推导
- pkgs = boot stages;
- pkgs = boot (stdenvStages {inherit crossSystem ...});
- pkgs = boot (import ../stdenv {inherit crossSystem ...})
- pkgs/stdenv/default.nix:
- pkgs = boot (import ./cross {inherit crossSystem ...})
- pkgs/stdenv/cross/default.nix:
- pkgs = boot (lib.init bootStages ++ [(...) (..crossSystem..) (..crossSystem..)])
- 包含了若干个stages,包括本地的bootStages的前几个stages、和crossSystem相关的3个stages(其实只有最后两个stages使用了crossSystem)
- 这里需要搞清楚boot是怎么调用stages的
- pkgs/top-level/default.nix:
- boot = import ../stdenv/bo(__raw=false或未定义)oter.nix { inherit lib allPackages; };
- boot = import ../stdenv/booter.nix { ...; allPackages = newArgs: import ./stage.nix ({inherit lib nixpkgsFun;} // newArgs);};
- pkgs/stdenv/booter.nix:
-
注释写了boot的功能,如下: 参数为一个函数数组,每个函数的输入为pkgset,输出要么是pkgset的参数(__raw=false或未定义),要么是pkgsset(__raw=true)。 (这里说的“pkgset的参数”会传给allPackages(是个函数,见上))
# Type: # [ pkgset -> (args to stage/default.nix) or ({ __raw = true; } // pkgs) ] # -> pkgset # # In english: This takes a list of function from the previous stage pkgset and # returns the final pkgset. Each of those functions returns, if `__raw` is # undefined or false, args for this stage's pkgset (the most complex and # important arg is the stdenv), or, if `__raw = true`, simply this stage's # pkgset itself.
-
- 所以最终还是得看下面两个使用了crossSystem的stages
- pkgs/stdenv/cross/default.nix:
-
pkgs = boot (lib.init bootStages ++ [(...) ❶(..crossSystem..) ❷(..crossSystem..)])
-
❶
# Build tool Packages (vanillaPackages: { ... stdenv = assert ...; vanillaPackages.stdenv.override { targetPlatform = crossSystem; }; ... })
输入是vanillaPackages,输出是buildPackages的参数(为生成下一级的输入pkgset即buildPackages)。 因此,这一级(即buildPackages,pkgsCross.
.buildPackages)的3个平台为: build host target localSystem localSystem crossSystem 因此这一级的包是用来构建各种交叉工具链的包。
注:nixpkgs里也有叫buildPackages的包,定义位于pkgs/top-level/stage.nix: buildPackages是pkgsBuildHost的别名,其中pkgs
。 -
❷
# Run Packages (buildPackages: let ... stdenNoCC = adaptStdenv (buildPackages.stdenv.override (old: rec { buildPlatform = localSystem; hostPlatform = crossSystem; targetPlatform = crossSystem; ... })); in { ... stdenv = let baseStdenv = stdenNoCC.override { cc = ... buildPackages.gcc; } in ... baseStdenv; ... })
这一级输入是buildPackages,输出就是我们平时pkgsCross.
的包集合了。 这一级(即pkgsCross. )的3个平台为 build host target localSystem crossSystem crossSystem 其中要注意的是,pkgsCross.
.stdenv.cc就是pkgsCross. .buildPackages.gcc。
-
引用
nixpkgs版本2022.01.20, commit hash: 7e149abe9db1509fa974bb2286f862a761ca0a07
nixpkgs/pkgs/top-level/default.nix
nixpkgs/lib/systems/default.nix: elaborate
nixpkgs/lib/systems/parse.nix: mkSkeletonFromList
nixpkgs/lib/systems/parse.nix: cpuTypes
nixpkgs/lib/systems/parse.nix: isCompatible
nixpkgs/lib/systems/parse.nix: kernels
nixpkgs/lib/systems/parse.nix: abis
安装NixOS
安装过程采用官方安装文档。 若已安装NixOS,则可跳过该步骤,直接看安装我的配置。
准备镜像
QEMU:
# 下载minimal ISO镜像:https://nixos.org/download.html
# 创建qemu硬盘(大小32GB)
qemu-img create -f qcow2 <output/path/to/nix.qcow2> 32G
# 将ISO安装到qemu硬盘
qemu-system-x86_64 -display gtk,window-close=off -vga virtio -device e1000,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5556-:22,smb=/home/xieby1/ -m 4G -smp 3 -enable-kvm -hda </path/to/nix.qcow2> -cdrom </path/to/nixos-minial.iso> -boot d &
物理机:
# 暂时未探究命令行连接wifi的方法
# 所以目前使用gnome版ISO,而非minimal ISO。
# 下载gnome ISO镜像:https://nixos.org/download.html
# 启动U盘制作
sudo dd if=<path/to/nixos.iso> of=</dev/your_usb>
sync
# 重启进入U盘系统
# 注:需要在BIOS中取消secure boot,否则U盘无法启动。
分区
进入ISO系统后,创建分区。 一共需要3个分区:启动分区,操作系统分区,swap分区。 QEMU和物理机单系统需要创建这3个分区。 物理机双系统中启动分区已有,只需创建剩下2个分区。
QEMU:
sudo bash
parted /dev/sda -- mklabel msdos
parted /dev/sda -- mkpart primary 1MiB -8GiB
parted /dev/sda -- mkpart primary linux-swap -8GiB 100%
物理机单系统:
parted /dev/sda -- mklabel gpt
parted /dev/sda -- mkpart primary 512MiB -8GiB
parted /dev/sda -- mkpart primary linux-swap -8GiB 100%
parted /dev/sda -- mkpart ESP fat32 1MiB 512MiB
parted /dev/sda -- set 3 esp on
物理机双系统:
注:用disk创建的文件系统,权限不对,安装完后会有各种奇怪的问题。 因此推荐命令行操作,或者disk之后再执行下一节的指令。
还未探索parted详细用法,目前使用disk软件可视化分区。
- 创建Ext4分区,取名为nixos
- 创建Other->swap分区
文件系统
mkfs.ext4 -L nixos /dev/<系统分区>
mkswap -L swap /dev/<swap分区>
swapon /dev/<swap分区>
mkfs.fat -F 32 -n boot /dev/<启动分区> # 物理机单系统
mount /dev/disk/by-label/nixos /mnt
mkdir -p /mnt/boot # 物理机单系统&双系统
mount /dev/disk/by-label/boot /mnt/boot # 物理机单系统&双系统
基础配置
- 生成配置文件
nixos-generate-config --root /mnt
- 修改/mnt/etc/nixos/configuration.nix,
- 修改名字
networking.hostName
- 启用代理
- QEMU中宿主机器的ip为10.0.2.2
- 安装过程中需要借助别的计算机或宿主机的的代理服务
- 部署完我的nixos配置后,将会有clash服务,可以用虚拟机的代理服务
networking.proxy.default = "http://user:password@proxy:port/";
networking.proxy.noProxy = "127.0.0.1,localhost,internal.domain";
- (QEMU和物理机单系统)取消以下注释以开启grub支持
boot.loader.grub.device = "/dev/sda";
- 取消防火墙,以便kdeconnect正常运行
networking.firewall.enable = false;
- (物理机双系统)自动探测操作系统启动项
boot.loader.grub.useOSProber = true;
- 添加用户
users.users.xieby1
- 添加软件
environment.systemPackages = with pkgs; [vim git];
- 修改名字
最后
nixos-install
reboot
重启之后,进入NixOS。
How To Config Linux Kernel in Nix
Basic configuration tricks referring to nixos.wiki: Linux Kernel.
The Override System in Kernel
What do the pkgs.linux.override
, pkgs.linux.overrideAttrs
override?
To answer these questions, we need first to explore how kernel derivation is generated.
When we type pkgs.linux
, how this derivation is generated by nix?
* pkgs.linux
* pkgs/top-level/all-packages:
* linuxPackages.kernel;
* (linuxPackages = linuxKernel.packageAliases.linux_default).kernel;
* (linuxPackages = (linuxKernel = recurseIntoAttrs (callPackage ./linux-kernels.nix { });).packageAliases.linux_default).kernel;
* (recurseIntoAttrs (callPackage ./linux-kernels.nix { });).packageAliases.linux_default.kernel;
* pkgs/top-level/linux-kernels.nix:
* packages.linux_6_6.kernel;
* (recurseIntoAttrs (vanillaPackages // rtPackages // rpiPackages // { ... })).linux_6_6.kernel
* (recurseIntoAttrs ({...; linux_6_6 = recurseIntoAttrs (packagesFor kernels.linux_6_6);...} // rtPackages // rpiPackages // { ... })).linux_6_6.kernel
* (packagesFor kernels.linux_6_6).kernel
* ((kernel_: {kernel = kernel_;}) kernels.linux_6_6).kernel
* kernels.linux_6_6
* ❶callPackage ../os-specific/linux/kernel/mainline.nix {
branch = "6.6";
kernelPatches = [
kernelPatches.bridge_stp_helper
kernelPatches.request_key_helper
];
};
* pkgs/os-specific/linux/kernel/mainline.nix
* buildLinux args'
* ❷buildLinux ((builtins.removeAttrs args [ "branch" ])
// {
inherit src version;
modDirVersion = lib.versions.pad 3 version;
extraMeta.branch = branch;
}
// (args.argsOverride or { }))
* (callPackage pkgs/os-specific/linux/kernel/generic.nix {}) args'
* overridableKernel args'
* (lib.makeOverridable ({...}: kernel.overrideAttrs (...))) args'
* kernel.overrideAttrs (...)
* ❸((callPackage ./manual-config.nix { inherit lib stdenv buildPackages; }) (basicArgs // { ... })).overrideAttrs (...)
因此
- ❶
override
is applied topkgs/os-specific/linux/kernel/mainline.nix
- ❷
argsOverride
inoverride (old: {argsOverride = {...} })
is applied topkgs/os-specific/linux/kernel/generic.nix
- ❸
overrideAttrs
is applied topkgs/os-specific/linux/kernel/manual-config.nix
Nix让你的团队成员不再受环境问题困扰
注:左下角菜单里可切换全屏
See all fhs-shell scripts on [Github repo: scripts/fhs-shell]({{ site.repo_url }}/scripts/fhs-shell)
#!/usr/bin/env -S nix-shell --keep miao
{ pkgs ? import <nixpkgs> {} }:
(pkgs.buildFHSUserEnv {
name = "dynamoRIO";
targetPkgs = pkgs: with pkgs; [
(hiPrio gcc)
snappy
zlib
zlib.dev
lz4
lz4.dev
libunwind
libunwind.dev
];
profile = ''
export CC=gcc
export CXX=g++
'';
}).env
#!/usr/bin/env -S nix-shell --keep miao
let
pkgs = import <nixpkgs> {};
fhs = pkgs.buildFHSUserEnv {
name = "hello";
targetPkgs = p: with p; [
hello
];
profile = ''
export MIAO=1
'';
};
in
fhs.env
#!/usr/bin/env -S nix-shell --keep miao
{ pkgs ? import <nixpkgs> {} }:
let
pin = builtins.derivation {
name = "pin-3.25";
system = builtins.currentSystem;
src = pkgs.fetchurl {
url = "https://software.intel.com/sites/landingpage/pintool/downloads/pin-3.25-98650-g8f6168173-gcc-linux.tar.gz";
hash = "sha256-Q8D0QSNLDly2XK+XFOYdMxbx5N33eGVzESGTCgWGX6E=";
};
builder = pkgs.writeShellScript "pin-builder" ''
# make mkdir and tar and other useful tools added to PATH
source ${pkgs.stdenv}/setup
mkdir -p $out
# strip leading directory
tar -xf $src --strip-components=1 --directory=$out
'';
};
in
(pkgs.buildFHSUserEnv {
name = "pin";
targetPkgs = pkgs: with pkgs; [
];
profile = ''
PATH+=":${pin}"
'';
}).env
#!/usr/bin/env -S nix-shell --keep miao
{ pkgs ? import <nixpkgs> {} }:
let
zigenv = import /home/xieby1/Codes/nix-zig-stdenv {
target = "x86_64-unknown-linux-musl";
};
noPrefixStaticStdenvCC = pkgs.runCommand "linkCC" {} ''
mkdir -p $out/bin
for file in ${pkgs.pkgsStatic.stdenv.cc}/bin/*; do
ln -s $file $out/bin/''${file##*-}
done
'';
in
(pkgs.buildFHSUserEnv {
name = "spec";
targetPkgs = pkgs: with pkgs; [
# (hiPrio zigenv.pkgs.stdenv.cc)
# (hiPrio clangStdenv.cc)
# (hiPrio gcc)
# (hiPrio pkgsStatic.stdenv.cc)
# noPrefixStaticStdenvCC
gfortran
# uclibc
# musl
# musl.dev
glibc.static
glibc.dev
];
}).env
#!/usr/bin/env -S nix-shell --keep miao
# https://ryantm.github.io/nixpkgs/builders/special/fhs-environments/
{ pkgs ? import <nixpkgs> {} }:
(pkgs.buildFHSUserEnv {
name = "simple-x11-env";
targetPkgs = pkgs: (with pkgs;
[ udev
alsa-lib
]) ++ (with pkgs.xorg;
[ libX11
libXcursor
libXrandr
]);
multiPkgs = pkgs: (with pkgs;
[ udev
alsa-lib
]);
runScript = "bash";
}).env
#!/usr/bin/env -S nix-shell --keep miao
# based on https://github.com/nix-community/nix-environments
# git commit: 40d9d98bab7750bb5a1a9a3b5bcc1c91a652f3be
{ pkgs ? import <nixpkgs> {} }:
let
name = "xilinx-fhs";
h_content = builtins.toFile "h_content" ''
# ${pkgs.lib.toUpper "${name} usage"}
**Commands**
* Show this help: `h`
* Start Vitis HLS IDE: `vitis_hls`
* Start Vitis HLS REPL: `vitis_hls -i`
**Files**
* Vitis HLS Doc: `2022.vitis_hls.ug1399.pdf`
* Local command doc: `Xilinx/Vitis/2022.2/doc/eng/man/`
* TODO: `ug871-vivado-high-level-synthesis-tutorial.pdf`
* TODO: `ug902-vivado-high-level-synthesis.pdf`
**Examples**
* [Vitis HLS examples](https://github.com/Xilinx/Vitis-HLS-Introductory-Examples)
* Run an example: `vitis_hls -f run_hls.tcl`
'';
_h_ = pkgs.writeShellScriptBin "h" ''
${pkgs.glow}/bin/glow ${h_content}
'';
in
(pkgs.buildFHSUserEnv {
inherit name;
targetPkgs = pkgs: with pkgs; [
_h_
bash
coreutils
zlib
lsb-release
stdenv.cc.cc
ncurses5
xorg.libXext
xorg.libX11
xorg.libXrender
xorg.libXtst
xorg.libXi
xorg.libXft
xorg.libxcb
xorg.libxcb
# common requirements
freetype
fontconfig
glib
gtk2
gtk3
# vitis_hls gcc needs
glibc.dev
# to compile some xilinx examples
opencl-clhpp
ocl-icd
opencl-headers
# from installLibs.sh
graphviz
(lib.hiPrio gcc)
unzip
nettools
];
multiPkgs = null;
profile = ''
export LC_NUMERIC="en_US.UTF-8"
source ~/Xilinx/Vitis_HLS/*/settings64.sh
h
'';
}).env
See all fhs-shell scripts on [Github repo: scripts/pkgs]({{ site.repo_url }}/scripts/pkgs)
# TODO: 7z need dynamical link 7z.so
# but static compilation prevents this behavior?
{pkgs ? import <nixpkgs> {}}:
let cross = import /home/xieby1/Codes/nix-zig-stdenv {
inherit pkgs;
target = "x86_64-unknown-linux-musl";
};
in
cross.pkgs.p7zip.overrideAttrs (old: {
postPatch = old.postPatch + ''
sed -i '/CC=/d' makefile.machine
sed -i '/CXX=/d' makefile.machine
'';
})
在NixOS上使用Android程序
TLDR: 使用Android Studio提供的Android Emulator体验最好,其次是使用QEMU+x86Android。
Anbox & Waydroid
Anbox & Waydroid属于将Android runtime移植到Linux上的项目。 都需要安装内核模块。
Anbox已未维护。
Waydroid需要Wayland支持。目前我还采用X11。
qemu + x86 Android
虚拟机,没有找到图形和音频的加速方法。基本可用的程度。
- vanilla x86 android: OK
- lineageOS: OK
- same to vanilla x86 android
- wechat not work
- phoenixOS: stuck in booting
# 安装在运行命令后添加-cdrom </path/to/iso> -boot -d
# 运行命令
qemu-system-x86_64 -m 4G -smp 3 -hda ~/Img/andx64_vcm141r5.qcow2 -enable-kvm -display gtk,window-close=off -device AC97
注:-vga virtio不能启动图形界面,原因未探索。
Google Android Emulator
使用nix提供的emulate-app.nix直接运行Google Android Emulator (魔改的QEMU)。
模拟器闪退无法启动,暂时没有尝试去解决。
参考
- https://nixos.wiki/wiki/Android
- SO: Run Android app in qemu-arm?
使用的nix脚本见android.nix
获取apk package和activity
aapt已经淘汰,现在使用的是aapt2,run by nix-alien。 例子,
nix-alien aapt2 dump badging </path/to/apk>
avdmanager找不到镜像
Error: Package path is not valid. Valid system image paths are:ository...
相关问题
- GH: avdmanager create avd fails with "Probably the SDK is read-only" #154898
- SO: error: package path is not valid. valid system image paths are:ository... null
从pkgs/development/mobile/androidenv/compose-android-packages.nix看, platformVersions, systemImageTypes, abiVersions需要和 pkgs/development/mobile/androidenv/repo.json 中的emulator项中的数据匹配。
看repo.json,而不是看sdkmanager --list
,不一定有,但能看到已安装。
不匹配,在nix-build中不会报错,运行过程报上面的错误。
模拟器闪退
和问题类似GH: androidenv.emulateApp fails to start emulator (libvulkan.so.1: full) #121146
报错内容,没解决
cannot add library /nix/store/yz1p6cw09h3im4z7wmx7nshi054fzhw4-emulator-30.8.4/libexec/android-sdk/emulator/qemu/linux-x86_64/lib64/vulkan/libvulkan.so: failed
added library /nix/store/yz1p6cw09h3im4z7wmx7nshi054fzhw4-emulator-30.8.4/libexec/android-sdk/emulator/lib64/vulkan/libvulkan.so
emulator: WARNING: Ignoring invalid http proxy: Bad format: invalid port number (must be decimal)
Device state has been reached
LLVM ERROR: Cannot select: intrinsic %llvm.x86.sse41.pblendvb
debug 方法
参考https://nixos.wiki/wiki/Nixpkgs/Create_and_debug_packages#How_to_install_from_the_local_repository
使用本地nixpkgs,修改,nix-shell看变量 例子:
nix-shell -E 'with import "/home/xieby1/temp-nixpkgs" {config.android_sdk.accept_license = true;}; (androidenv.composeAndroidPackages {includeSystemImages =true; platformVersions=["16"];}).androidsdk'
nix expression调用关系
- emulate-app.nix
- compose-android-packages.nix
- tools.nix
- tools/26.nix
- deploy-androidpackage.nix
- tools/26.nix
- tools.nix
- compose-android-packages.nix
PCI bus not available for hda
https://stackoverflow.com/questions/69297141/android-11-emulator-gives-pci-bus-not-available-for-hda
修改./result/bin/run-test-emulator添加-qemu -machine virt到Launch the emulator的指令后
/nix/store/0q68cfq7rnbw752l89fkxf425v1pb2r6-androidsdk/libexec/android-sdk/emulator/emulator -avd device -no-boot-anim -port $port $NIX_ANDROID_EMULATORFLAGS -qemu -machine virt &
Google Android Studio
使用Android Studio的AVD安装Android emulator最省心。
可以使用微信(公众号闪退),语音视频流畅,小游戏流畅。
redroid
暂时没成功
文档并未提供和各个参数相关的源文件?
#!/usr/bin/env nix-build
# https://nixos.wiki/wiki/Android
# Building Android applications with the Nix package manager: https://sandervanderburg.blogspot.com/2014/02/reproducing-android-app-deployments-or.html
let
# current nixpkgs-unstable
pkgs = import (with import <nixpkgs> {}; fetchFromGitHub {
owner = "NixOS";
repo = "nixpkgs";
rev = "1c851e8c92b76a00ce84167984a7ec7ba2b1f29c";
hash = "sha256-vRxti8pOuXS0rJmqjbD8ueEEFXWSK22ISHoCWkhgzzg=";
}){
config.android_sdk.accept_license = true;
config.allowUnfree = true;
};
in pkgs.androidenv.emulateApp {
name = "androidEmuApp";
app = pkgs.fetchurl {
url = "https://github.com/SimpleMobileTools/Simple-Calendar/releases/download/6.13.5/calendar-release.apk";
sha256 = "12vzcd6klnk38b55szmd5a8ydc70fk6aak31qvlild83jy9z21zk";
};
enableGPU = false;
# get these info by `pkgs/development/mobile/androidenv/repo.json`
# see if installed `sdkmanager --list`
platformVersion = "32";
abiVersion = "x86";
systemImageType = "google_apis";
package = "com.simplemobiletools.calendar.pro";
avdHomeDir = "$HOME/.android";
sdkExtraArgs = {
includeSystemImages = true;
};
}
let
pkgs = import <nixpkgs> {};
squashfuse-with-headers = pkgs.pkgsStatic.squashfuse.overrideAttrs (old: {
src = pkgs.fetchurl {
url = "https://github.com/vasi/squashfuse/archive/e51978c.tar.gz";
hash = "sha256-9UQCmtMNj73k5FQMV0uM3G04uU3wJamNhVGpRB8H00E=";
};
postInstall = ''
mkdir -p $out/include/squashfuse
cp *.h $out/include/squashfuse
'';
});
srcRev = "c1ea7509bc179a05d907baca64f41875662f35f2";
in pkgs.stdenv.mkDerivation {
name = "appimage-runtime";
src = pkgs.fetchFromGitHub {
owner = "AppImage";
repo = "type2-runtime";
rev = "${srcRev}";
sha256 = "1gr853iz1x6pgyav3w1kqaaaz2ybbx67dcg74kj54yrwlakrh165";
};
nativeBuildInputs = with pkgs; [
pkg-config
];
buildInputs = (with pkgs.pkgsStatic; [
squashfuse-with-headers
fuse
zstd
zlib
lz4.out
lzo
lzma.out
]) ++ (with pkgs; [
glibc.static
]);
sourceRoot = "source/src/runtime";
buildPhase = ''
export CFLAGS="-std=gnu99 -s -Os -D_FILE_OFFSET_BITS=64 -DGIT_COMMIT=\"${srcRev}\" -T data_sections.ld -ffunction-sections -fdata-sections -Wl,--gc-sections -static"
export LIBS="-lsquashfuse -lsquashfuse_ll -lzstd -lz -llz4 -llzo2 -llzma"
$CC -I${squashfuse-with-headers}/include/squashfuse -I${pkgs.pkgsStatic.fuse}/include/fuse -o runtime-fuse2.o -c $CFLAGS runtime.c
$CC $CFLAGS runtime-fuse2.o $LIBS -lfuse -o runtime-fuse2
'';
installPhase = ''
mkdir -p $out/bin
cp runtime-fuse2 $out/bin/
'';
}
# in pkgs.mkShell {
# name = "appimage-runtime";
# packages = (with pkgs.pkgsStatic; [
# squashfuse-with-headers
# fuse
# zstd
# zlib
# lz4.out
# lzo
# lzma.out
# ]) ++ (with pkgs; [
# glibc.static
# pkg-config
# ]);
# }
# in (pkgs.buildFHSUserEnv {
# name = "appimage-runtime-fhs";
# targetPkgs = pkgs: (with pkgs.pkgsStatic; [
# squashfuse-with-headers
# fuse
# zstd
# zlib
# lz4.out
# lzo
# lzma.out
# ]) ++ (with pkgs; [
# glibc.static
# pkg-config
# ]);
# }).env
#!/usr/bin/env -S nix-build -o coremarks
{pkgs ? import <nixpkgs> {}}:
let
# function mkCoremark
mkCoremark = {
pkgs ? import <nixpkgs> {},
stdenv ? pkgs.stdenv,
simple ? false,
}:
let
name = "coremark";
variant = pkgs.lib.concatStrings [
"${stdenv.targetPlatform.config}"
# TODO: all zig-env are static?
# (if stdenv.targetPlatform.isStatic then ".static" else "")
(if simple then ".simple" else "")
];
in
stdenv.mkDerivation {
inherit name;
src = pkgs.fetchFromGitHub {
owner = "eembc";
repo = "coremark";
rev = "d26d6fdcefa1f9107ddde70024b73325bfe50ed2";
sha256 = "0kd6bnrnd3f325ypxzn0w5ii4fmc98h16sbvvjikvzhm78y60wz3";
};
preBuild = ''
# no float point insts
export CFLAGS="-DHAS_FLOAT=0"
# simple assumes CC = gcc, this is a bug!
sed -i '/CC =/d' simple/core_portme.mak
${if simple
then "export PORT_DIR=simple"
else ""}
'';
buildFlags = ["compile"];
installPhase = ''
mkdir -p $out/bin
mv coremark.exe $out/bin/${name}.${variant}.exe
'';
};
zig-env-src = pkgs.fetchFromGitHub {
owner = "Cloudef";
repo = "nix-zig-stdenv";
rev = "6de72ec32ecf0cfb9ad9dab5a8400d532e17f8c5";
hash = "sha256-hQHOzjkHWO5YxQb3mgZJOfyIuvbiLFocVCMK/A9HTic=";
};
in
pkgs.symlinkJoin {
name = "coremarks";
paths = [
# x86_64 linux
(mkCoremark {
inherit (import zig-env-src {
target = "x86_64-unknown-linux-gnu";
}) stdenv;
})
# x86_64 linux static
(mkCoremark {
inherit (import zig-env-src {
target = "x86_64-unknown-linux-musl";
}) stdenv;
})
# aarch64 linux
(mkCoremark {
inherit (import zig-env-src {
target = "aarch64-unknown-linux-gnu";
}) stdenv;
})
# aarch64 linux static
(mkCoremark {
inherit (import zig-env-src {
target = "aarch64-unknown-linux-musl";
}) stdenv;
})
# riscv64 linux
# (mkCoremark {
# inherit (import zig-env-src {
# target = "riscv64-unknown-linux-gnu";
# }) stdenv;
# })
# riscv64 linux static
(mkCoremark {
inherit (import zig-env-src {
target = "riscv64-unknown-linux-musl";
}) stdenv;
})
# x86_64 windows
(mkCoremark {
inherit (import zig-env-src {
target = "x86_64-w64-mingw32";
}) stdenv;
simple = true;
})
# x86_64 darwin can only compiled on x86_64/aarch64 darwin
# https://github.com/NixOS/nixpkgs/issues/165804
# while it is possible compile manually inside darling
#(mkCoremark {
# stdenv = pkgs.pkgsCross.x86_64-darwin.stdenv;
#})
];
}
#!/usr/bin/env -S nix-env -i -f
# xieby1: 2022.05.16
with (import <nixpkgs> {crossSystem = "mips-linux";}); {
gdb = lib.lowPrio buildPackages.gdb;
gcc = lib.lowPrio buildPackages.gcc;
}
#!/usr/bin/env nix-build
let
pkgs = import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/e60c3e2abb8ae9df3d89820c706cc736fad01ff7.tar.gz";
sha256 = "0vyjpf1jw4cvw7kfbk055faq08q4swz6v1h2mf9zw4r8frhqa73w";
}) {};
in
pkgs.pkgsCross.aarch64-multiplatform.pkgsStatic.glib
#!/usr/bin/env -S nix-build -o fhs_helloworld
# xieby1: 2022.11.17
# inspired by
# https://discourse.nixos.org/t/derivation-that-builds-standard-linux-binaries/21557/4
# https://github.com/NixOS/nixpkgs/compare/master...ElvishJerricco:nixpkgs:run-in-fhs
# TODO-1:
# I have copy all dependencies to a directory (ld-linux-x86-64.so.2, libc.so)
# But result cannot run in chroot, error message is `cannot find libc.so`.
# TODO-2:
# I didn't find a proper package/bundle tool
# * nix-bundle: not work
# * appimage: depends on libfuse.so, cannot run directly on nixos
{ pkgs ? import <nixpkgs> {} }:
let
name = "helloworld";
fhsEnv = pkgs.buildFHSUserEnv {
name = "${name}-fhs";
targetPkgs = pkgs: with pkgs; [
gcc
# gcc-unwrapped binutils-unwrapped
glibc
glibc.dev
];
# refer to https://discourse.nixos.org/t/using-a-raw-gcc-inside-buildfhsuserenv/12864
runScript = (pkgs.writeShellScript "${name}-fhsbuilder" ''
# For gcc-unwrapped and binutils-unwrapped
# export LIBRARY_PATH=/usr/lib
# export C_INCLUDE_PATH=/usr/include
# export CPLUS_INCLUDE_PATH=/usr/include
# export CMAKE_LIBRARY_PATH=/usr/lib
# export CMAKE_INCLUDE_PATH=/usr/include
## TODO: not work? have to add gcc -Wl,--dynamic-linker=/usr/lib64/ld-linux-x86-64.so.2 ?
# export LDFLAGS=--dynamic-linker=/usr/lib64/ld-linux-x86-64.so.2
# For gcc
export NIX_LDFLAGS="--dynamic-linker=/usr/lib64/ld-linux-x86-64.so.2"
gcc $src -o $out
'');
};
in
builtins.derivation {
inherit name;
system = builtins.currentSystem;
src = builtins.toFile "${name}.c" ''
#include <stdio.h>
int main(void) {
printf("Hello, world! \n");
return 0;
}
'';
builder = "${fhsEnv}/bin/${fhsEnv.name}";
}
let
name = "perl";
pkgs = import <nixpkgs> {};
fhsEnv = pkgs.buildFHSUserEnv {
name = "${name}-fhs";
targetPkgs = pkgs: with pkgs; [
gnumake
];
runScript = (pkgs.writeShellScript "${name}-fhsbuilder" ''
ls
cd $src/tools/src
ls
DOPERL=1 ./buildtools
'');
};
in builtins.derivation {
inherit name;
system = builtins.currentSystem;
src = /home/xieby1/Codes/spec2000;
builder = "${fhsEnv}/bin/${fhsEnv.name}";
}
#!/usr/bin/env -S nix-build -o nix-binary-tarballs
Nix official installer (new)
Use nix (>2.18) binary-tarball script, which has not been merged into mainline nixpkgs.
See nix-binary-tarballs.nix for current nix binary-tarball script.
let
pkgs = import <nixpkgs> {};
nix_src = pkgs.fetchFromGitHub {
owner = "NixOS";
repo = "nix";
rev = "d0c7da131fb64526bc72144949b6955c25367d92";
hash = "sha256-Z4RlluZjNXH5BdJiXoe0k43Ry9ptK3jHAsKjlQ3jVZg=";
};
in pkgs.symlinkJoin {
name = "nix-binary-tarballs";
paths = [
(pkgs.callPackage "${nix_src}/scripts/binary-tarball.nix" {})
(pkgs.callPackage "${nix_src}/scripts/binary-tarball.nix" {
nix = pkgs.pkgsCross.riscv64.nix;
system = pkgs.pkgsCross.riscv64.nix.stdenv.system;
})
(pkgs.callPackage "${nix_src}/scripts/binary-tarball.nix" {
nix = pkgs.pkgsCross.loongarch64-linux.nix;
system = pkgs.pkgsCross.loongarch64-linux.nix.stdenv.system;
})
];
}
#!/usr/bin/env -S nix-build -o nix-binary-tarballs
Nix official installer
The binaryTarball
function is extracted from nix-2.18's flake.nix
Noted: Although the latest nix has seperate binaryTarball as a independent file, current unstable nixpkgs still use nix-2.18, which embed binaryTarball function in nix/flake.nix. If reuse the binaryTarball file in new nix, then this script can be hugely simplified, e.g. nix-binary-tarballs-new.nix. As long as, new nix is merged to mainline nixpkgs, I will deprecate this script and adopt the new script.
let
pkgs = import <nixpkgs> {};
binaryTarball =
{ nix
, buildPackages
, cacert
}:
let
installerClosureInfo = buildPackages.closureInfo { rootPaths = [ nix cacert ]; };
in buildPackages.runCommand "nix-binary-tarball-${nix.version}" {
#nativeBuildInputs = lib.optional (system != "aarch64-linux") shellcheck;
meta.description = "Distribution-independent Nix bootstrap binaries for ${nix.stdenv.system}";
} ''
cp ${installerClosureInfo}/registration $TMPDIR/reginfo
cp ${nix.src}/scripts/create-darwin-volume.sh $TMPDIR/create-darwin-volume.sh
substitute ${nix.src}/scripts/install-nix-from-closure.sh $TMPDIR/install \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
substitute ${nix.src}/scripts/install-darwin-multi-user.sh $TMPDIR/install-darwin-multi-user.sh \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
substitute ${nix.src}/scripts/install-systemd-multi-user.sh $TMPDIR/install-systemd-multi-user.sh \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
substitute ${nix.src}/scripts/install-multi-user.sh $TMPDIR/install-multi-user \
--subst-var-by nix ${nix} \
--subst-var-by cacert ${cacert}
if type -p shellcheck; then
# SC1090: Don't worry about not being able to find
# $nix/etc/profile.d/nix.sh
shellcheck --exclude SC1090 $TMPDIR/install
shellcheck $TMPDIR/create-darwin-volume.sh
shellcheck $TMPDIR/install-darwin-multi-user.sh
shellcheck $TMPDIR/install-systemd-multi-user.sh
# SC1091: Don't panic about not being able to source
# /etc/profile
# SC2002: Ignore "useless cat" "error", when loading
# .reginfo, as the cat is a much cleaner
# implementation, even though it is "useless"
# SC2116: Allow ROOT_HOME=$(echo ~root) for resolving
# root's home directory
shellcheck --external-sources \
--exclude SC1091,SC2002,SC2116 $TMPDIR/install-multi-user
fi
chmod +x $TMPDIR/install
chmod +x $TMPDIR/create-darwin-volume.sh
chmod +x $TMPDIR/install-darwin-multi-user.sh
chmod +x $TMPDIR/install-systemd-multi-user.sh
chmod +x $TMPDIR/install-multi-user
dir=nix-${nix.version}-${nix.stdenv.system}
fn=$out/$dir.tar.xz
mkdir -p $out/nix-support
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
tar cvfJ $fn \
--owner=0 --group=0 --mode=u+rw,uga+r \
--mtime='1970-01-01' \
--absolute-names \
--hard-dereference \
--transform "s,$TMPDIR/install,$dir/install," \
--transform "s,$TMPDIR/create-darwin-volume.sh,$dir/create-darwin-volume.sh," \
--transform "s,$TMPDIR/reginfo,$dir/.reginfo," \
--transform "s,$NIX_STORE,$dir/store,S" \
$TMPDIR/install \
$TMPDIR/create-darwin-volume.sh \
$TMPDIR/install-darwin-multi-user.sh \
$TMPDIR/install-systemd-multi-user.sh \
$TMPDIR/install-multi-user \
$TMPDIR/reginfo \
$(cat ${installerClosureInfo}/store-paths)
'';
in pkgs.symlinkJoin {
name = "nix-binary-tarballs";
paths = [
# local nix installer
(pkgs.callPackage binaryTarball {})
# riscv64 nix installer
(pkgs.callPackage binaryTarball {nix=pkgs.pkgsCross.riscv64.nix;})
# loongarch64 nix installer
(pkgs.callPackage binaryTarball {nix=pkgs.pkgsCross.loongarch64-linux.nix;})
];
}
🐳Nix Docker🐋 for Multiple ISAs
This script is inspired by https://github.com/nix-community/docker-nixpkgs/images/nix
currently: this riscv64 nix docker can nix-env -iA nixpkgs.hello/tmux
and so on,
which is completely built from source including toolchains (stdenv) in x86/aarch64/riscv64/...
{ pkgs ? import <nixpkgs> {}
, pkgsCross ? pkgs
, useTmux ? true
}:
let
name = "nix-docker-${pkgsCross.stdenv.system}";
image = pkgs.dockerTools.buildImageWithNixDb {
inherit name;
copyToRoot = pkgs.buildEnv {
name = "image-root";
paths = (with pkgsCross; [
bashInteractive
cacert
coreutils
file
gitMinimal
gnutar
nix
openssh
vim
wget
]
++ lib.optional useTmux (tmux.override {withSystemd=false;})
) ++ [
./imageFiles
];
};
extraCommands = ''
# for /usr/bin/env
mkdir usr
ln -s bin usr/bin
# make sure /tmp exists
mkdir -m 1777 tmp
# need a HOME
mkdir -vp root
'';
config = {
Cmd = if useTmux
then [ "/bin/tmux" ]
else [ "/bin/bash" ];
Env = [
"NIX_BUILD_SHELL=/bin/bash"
"PAGER=cat"
"PATH=/bin"
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
"USER=root"
];
};
};
in pkgs.writeShellScriptBin name ''
command -v podman &> /dev/null || echo "podman not found TODO: install" || exit 1
outName="$(basename ${image})"
outHash=$(echo "$outName" | cut -d - -f 1)
imageName=localhost/${name}:$outHash
# check whether image has been loaded
podman images $imageName | grep ${name} | grep $outHash &> /dev/null
# image has not been loaded, then load it
if [[ $? != 0 ]]; then
podman load -i ${image}
fi
BINFMTS=""
for binfmt in /run/binfmt/*; do
BINFMTS+=" -v $(realpath $binfmt):$binfmt"
done
containerName=${name}-$outHash
# run container
OPTS=(
"--name=$containerName"
"$BINFMTS"
"--network=host"
"-it"
"$imageName"
)
eval "podman run ''${OPTS[@]}"
podman commit $containerName $imageName
podman rm $containerName
''
#!/usr/bin/env nix-build
Instantiatio of nix dockers
For more details, see default.nix
{ pkgs ? import <nixpkgs> {}
, ...
}: [
x86_64 docker
(import ./. {inherit pkgs; pkgsCross=pkgs.pkgsCross.gnu64;})
aarhc64 docker
(import ./. {inherit pkgs; pkgsCross=pkgs.pkgsCross.aarch64-multiplatform;})
riscv64 docker
(import ./. {inherit pkgs; pkgsCross=pkgs.pkgsCross.riscv64;})
]
let
flake-compat = import (builtins.fetchTarball {
url = "https://github.com/edolstra/flake-compat/archive/0f9255e01c2351cc7d116c072cb317785dd33b33.tar.gz";
sha256 = "0m9grvfsbwmvgwaxvdzv6cmyvjnlww004gfxjvcl806ndqaxzy4j";
});
nix2nvchad = flake-compat {
src= builtins.fetchTarball {
url = "https://github.com/nix-community/nix4nvchad/archive/360ff667893eab066b3db906a856de2956fc710e.tar.gz";
sha256 = "01gvcg7nhzpizp4yzvww2x42i1ifsb7sygfwmqzrshqz47p1ir5y";
};
};
in nix2nvchad.defaultNix.packages."${builtins.currentSystem}".default
#!/usr/bin/env nix-build
# xieby1: 2022.05.24
# build static qemu (current version 6.1.1) into ./result/
# $ nix-build <this-file> -A qemu
# build static qemu-3.1.0 into ./result/
# $ nix-build <this-file> -A qemu31
# build and install static qemu
# $ nix-env -i -f <this-file> -A qemu
# build and install static qemu-3.1.0
# $ nix-env -i -f <this-file> -A qemu31
let
# https://lazamar.co.uk/nix-versions
pinnedPkgsForQemu31Src = builtins.fetchTarball {
name = "nixos-20.03-with-qemu-3.1.0";
url = "https://github.com/NixOS/nixpkgs/archive/81d4e65891f92e8e72c244da663c83c1e40dc919.tar.gz";
sha256 = "0dk1k1zqy2bnp9gsy9mdxk0idkazyvnmqrj2jpbwzfnhjzpmzq1w";
};
pinnedPkgsSrc = builtins.fetchTarball {
name = "nixos-static-qemu";
url = "https://github.com/nixos/nixpkgs/archive/e7d63bd0d50df412f5a1d8acfa3caae75522e347.tar.gz";
sha256 = "132pc4f9ixisyv4117p2jirmlyl6sd76bfaz33rhlcwakg7bhjm7";
};
pkgsForQemu31 = import pinnedPkgsForQemu31Src {};
pkgs = import pinnedPkgsSrc {};
mypkgs = import pinnedPkgsSrc {
overlays = [(self: super: {
cdparanoiaIII = super.pkgsStatic.cdparanoiaIII.overrideAttrs (old: {
preConfigure = old.preConfigure + ''
cp ${super.gnu-config}/config.sub configure.sub
cp ${super.gnu-config}/config.guess configure.guess
'';
# Makefile needs this to compile static
STATIC="TRUE";
buildPhase = ''
make lib
make cdparanoia
'';
preInstallPhases = ["preInstallPhase"];
preInstallPhase = ''
sed -i '/so/d' Makefile
'';
});
liburing = super.pkgsStatic.liburing.overrideDerivation (old: {
configureFlags = "";
ENABLE_SHARED = 0;
});
# p11-kit cannot be used as a static library
# https://github.com/p11-glue/p11-kit/issues/355
p11-kit = super.pkgsMusl.p11-kit;
# gnutls depends on p11-kit
gnutls = super.pkgsMusl.gnutls;
pam = super.pkgsStatic.openpam;
# support both static and shared
libselinux = (super.pkgsMusl.libselinux.override {
libsepol = super.pkgsStatic.libsepol;
}).overrideAttrs (old: {
makeFlags = old.makeFlags ++ [
"LIBDIR=$(out)/lib"
];
});
glib = (super.pkgsStatic.glib.override {
meson = pkgs.meson;
ninja = pkgs.ninja;
pkg-config = pkgs.pkg-config;
perl = pkgs.perl;
python3 = pkgs.python3;
gettext = pkgs.gettext;
gtk-doc = pkgs.gtk-doc;
docbook_xsl = pkgs.docbook_xsl;
docbook_xml_dtd_45 = pkgs.docbook_xml_dtd_45;
libxml2 = pkgs.libxml2;
}).overrideAttrs (old: {
outputs = super.lib.lists.remove "devdoc" old.outputs;
buildInputs = old.buildInputs ++ [
super.pkgsStatic.libsepol
];
preBuild = ''
sed -i "s/get_option('libmount')/get_option('libmount'), static: true/g" ../meson.build
sed -i "s/get_option('selinux')/get_option('selinux'), static: true/g" ../meson.build
'';
# no devdoc from non-static glibc
# ${pname} & ${version} is correct due to lazy assignment
postInstall = pkgs.glib.postInstall;
});
gtk3 = super.pkgsStatic.gtk3.override {
trackerSupport = false;
cupsSupport = false;
withGtkDoc = false;
# nativeBuildInputs
inherit (pkgs) gettext gobject-introspection makeWrapper meson ninja
pkg-config python3 sassc docbook_xml_dtd_43 docbook-xsl-nons gtk-doc libxml2;
};
qemu = ((super.pkgsStatic.qemu.override {
alsaSupport = false;
spiceSupport = false;
sdlSupport = false;
smartcardSupport = false;
gtkSupport = false;
pulseSupport = false;
# nativeBuildInputs
makeWrapper = pkgs.makeWrapper;
python = pkgs.python3;
pkg-config = pkgs.pkg-config;
flex = pkgs.flex;
bison = pkgs.bison;
meson = pkgs.meson;
ninja = pkgs.ninja;
perl = pkgs.perl;
}).overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs ++ [
pkgs.binutils
# perl as nativeBuildInputs has been added in nixpkgs master
# while it is backported to nixpkgs 21.11 (nixos 21.11).
# If without perl as nativeBuildInputs,
# ./scripts/shaderinclude.pl can not be patchShebangs'ed.
pkgs.perl # without it cannot patchShebangs
];
# qemu-6.1.1 has contained sigrtminmax patch, can not be patched again
patches = builtins.filter (
x: ! super.lib.hasSuffix "sigrtminmax.patch" x
) old.patches;
})).overrideDerivation (old: let
# qemu configure uses "--static" instead of standard "--disable-shared" and "--enable-static"
configureFlags_no_DS = super.lib.lists.remove "--disable-shared" old.configureFlags;
configureFlags_no_DS_no_ES = super.lib.lists.remove "--enable-static" configureFlags_no_DS;
in {
configureFlags = configureFlags_no_DS_no_ES ++ [
"--static"
# "--target-list-exclude="
"--target-list=x86_64-softmmu"
];
});
qemu31 = (((super.callPackage (
pinnedPkgsForQemu31Src + "/pkgs/applications/virtualization/qemu/default.nix"
) {
inherit (super.darwin.apple_sdk.frameworks) CoreServices Cocoa Hypervisor;
inherit (super.darwin.stubs) rez setfile;
}).override {
# In nixpkgs 20.03, stdenv contains lib attr.
stdenv = pkgs.pkgsStatic.stdenv // {lib = super.lib;};
alsaLib = null;
spiceSupport = false;
sdlSupport = false;
smartcardSupport = false;
gtkSupport = false;
pulseSupport = false;
# nativeBuildInputs
makeWrapper = pkgs.makeWrapper;
python2 = pkgs.python2;
pkgconfig = pkgs.pkgconfig;
flex = pkgs.flex;
bison = pkgs.bison;
perl = pkgs.perl;
}).overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs ++ [
# Several issues report ld version >= 2.34 will failed due to
# PHDR segment not covered by LOAD segment.
# https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/issues/122
# https://github.com/genodelabs/genode/issues/4003
# So I downgrade ld version < 2.34.
# I still do not figure out why the same version ld in
# qemu 6.1.1 static works correctly?
pkgsForQemu31.binutils
];
})).overrideDerivation (old: let
# drop audio configure flag
configureFlags_no_audio = builtins.filter (
x: ! super.lib.hasPrefix "--audio-drv-list" x
) old.configureFlags;
# qemu configure uses "--static" instead of standard "--disable-shared" and "--enable-static"
configureFlags_no_DS = super.lib.lists.remove "--disable-shared" configureFlags_no_audio;
configureFlags_no_DS_no_ES = super.lib.lists.remove "--enable-static" configureFlags_no_DS;
in {
configureFlags = configureFlags_no_DS_no_ES ++ [
# "--static"
# "--target-list-exclude="
"--target-list=x86_64-softmmu"
"--disable-gnutls"
"--disable-tools"
];
makeFlags = [
# "V=1" # qemu Makefile verbose
# It is neccessary to use ld in binutils, otherwise pc-bios build will fail.
# In qemu 6.1.1 static, it is using ld in binutils, instead of ld from musl-gcc
"AR=${pkgs.binutils-unwrapped}/bin/ar"
"AS=${pkgs.binutils}/bin/as"
"LD=${pkgs.binutils}/bin/ld"
"NIX_BINTOOLS=${pkgs.binutils}"
];
});
})];
};
in
{
inherit (mypkgs.pkgsStatic) qemu qemu31;
}
#!/usr/bin/env nix-build
# xieby1: 2022.05.24
# build static qemu (current version 6.1.1) into ./result/
# $ nix-build <this-file> -A qemu
# build static qemu-3.1.0 into ./result/
# $ nix-build <this-file> -A qemu31
# build and install static qemu
# $ nix-env -i -f <this-file> -A qemu
# build and install static qemu-3.1.0
# $ nix-env -i -f <this-file> -A qemu31
let
pinnedPkgsSrc = builtins.fetchTarball {
name = "nixos-static-qemu";
url = "https://github.com/nixos/nixpkgs/archive/e7d63bd0d50df412f5a1d8acfa3caae75522e347.tar.gz";
sha256 = "132pc4f9ixisyv4117p2jirmlyl6sd76bfaz33rhlcwakg7bhjm7";
};
# pkgs = import pinnedPkgsSrc {};
# mypkgs = import pinnedPkgsSrc {
pkgs = import <nixpkgs> {};
mypkgs = import <nixpkgs> {
overlays = [(self: super: {
cdparanoiaIII = super.pkgsStatic.cdparanoiaIII.overrideAttrs (old: {
preConfigure = old.preConfigure + ''
cp ${super.gnu-config}/config.sub configure.sub
cp ${super.gnu-config}/config.guess configure.guess
'';
# Makefile needs this to compile static
STATIC="TRUE";
buildPhase = ''
make lib
make cdparanoia
'';
preInstallPhases = ["preInstallPhase"];
preInstallPhase = ''
sed -i '/so/d' Makefile
'';
});
liburing = super.pkgsStatic.liburing.overrideDerivation (old: {
configureFlags = "";
ENABLE_SHARED = 0;
});
# p11-kit cannot be used as a static library
# https://github.com/p11-glue/p11-kit/issues/355
p11-kit = super.pkgsMusl.p11-kit;
# gnutls depends on p11-kit
gnutls = super.pkgsMusl.gnutls;
pam = super.pkgsStatic.openpam;
# support both static and shared
libselinux = (super.pkgsMusl.libselinux.override {
libsepol = super.pkgsStatic.libsepol;
}).overrideAttrs (old: {
makeFlags = old.makeFlags ++ [
"LIBDIR=$(out)/lib"
];
});
glib = (super.pkgsStatic.glib.override {
meson = pkgs.meson;
ninja = pkgs.ninja;
pkg-config = pkgs.pkg-config;
perl = pkgs.perl;
python3 = pkgs.python3;
gettext = pkgs.gettext;
gtk-doc = pkgs.gtk-doc;
docbook_xsl = pkgs.docbook_xsl;
docbook_xml_dtd_45 = pkgs.docbook_xml_dtd_45;
libxml2 = pkgs.libxml2;
}).overrideAttrs (old: {
outputs = super.lib.lists.remove "devdoc" old.outputs;
buildInputs = old.buildInputs ++ [
super.pkgsStatic.libsepol
];
preBuild = ''
sed -i "s/get_option('libmount')/get_option('libmount'), static: true/g" ../meson.build
sed -i "s/get_option('selinux')/get_option('selinux'), static: true/g" ../meson.build
'';
# no devdoc from non-static glibc
# ${pname} & ${version} is correct due to lazy assignment
postInstall = pkgs.glib.postInstall;
});
gtk3 = super.pkgsStatic.gtk3.override {
trackerSupport = false;
cupsSupport = false;
withGtkDoc = false;
# nativeBuildInputs
inherit (pkgs) gettext gobject-introspection makeWrapper meson ninja
pkg-config python3 sassc docbook_xml_dtd_43 docbook-xsl-nons gtk-doc libxml2;
};
qemu = ((super.pkgsStatic.qemu.override {
alsaSupport = false;
spiceSupport = false;
sdlSupport = false;
smartcardSupport = false;
gtkSupport = false;
pulseSupport = false;
# nativeBuildInputs
makeWrapper = pkgs.makeWrapper;
python = pkgs.python3;
pkg-config = pkgs.pkg-config;
flex = pkgs.flex;
bison = pkgs.bison;
meson = pkgs.meson;
ninja = pkgs.ninja;
perl = pkgs.perl;
}).overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs ++ [
pkgs.binutils
# perl as nativeBuildInputs has been added in nixpkgs master
# while it is backported to nixpkgs 21.11 (nixos 21.11).
# If without perl as nativeBuildInputs,
# ./scripts/shaderinclude.pl can not be patchShebangs'ed.
pkgs.perl # without it cannot patchShebangs
];
# qemu-6.1.1 has contained sigrtminmax patch, can not be patched again
patches = builtins.filter (
x: ! super.lib.hasSuffix "sigrtminmax.patch" x
) old.patches;
})).overrideDerivation (old: let
# qemu configure uses "--static" instead of standard "--disable-shared" and "--enable-static"
configureFlags_no_DS = super.lib.lists.remove "--disable-shared" old.configureFlags;
configureFlags_no_DS_no_ES = super.lib.lists.remove "--enable-static" configureFlags_no_DS;
in {
configureFlags = configureFlags_no_DS_no_ES ++ [
"--static"
# "--target-list-exclude="
"--target-list=x86_64-softmmu"
];
});
})];
};
in
{
inherit (mypkgs.pkgsStatic.pkgsCross.riscv64) qemu;
}
#!/usr/bin/env nix-build
# xieby1: 2022.05.24
# build static qemu (current version 6.1.1) into ./result/
# $ nix-build <this-file> -A qemu
# build static qemu-3.1.0 into ./result/
# $ nix-build <this-file> -A qemu31
# build and install static qemu
# $ nix-env -i -f <this-file> -A qemu
# build and install static qemu-3.1.0
# $ nix-env -i -f <this-file> -A qemu31
let
pinnedPkgsSrc = builtins.fetchTarball {
name = "nixos-static-qemu";
url = "https://github.com/nixos/nixpkgs/archive/e7d63bd0d50df412f5a1d8acfa3caae75522e347.tar.gz";
sha256 = "132pc4f9ixisyv4117p2jirmlyl6sd76bfaz33rhlcwakg7bhjm7";
};
# pkgs = import pinnedPkgsSrc {};
# mypkgs = import pinnedPkgsSrc {
pkgs = import <nixpkgs> {};
mypkgs = import <nixpkgs> {
overlays = [(self: super: {
cdparanoiaIII = super.pkgsStatic.cdparanoiaIII.overrideAttrs (old: {
preConfigure = old.preConfigure + ''
cp ${super.gnu-config}/config.sub configure.sub
cp ${super.gnu-config}/config.guess configure.guess
'';
# Makefile needs this to compile static
STATIC="TRUE";
buildPhase = ''
make lib
make cdparanoia
'';
preInstallPhases = ["preInstallPhase"];
preInstallPhase = ''
sed -i '/so/d' Makefile
'';
});
liburing = super.pkgsStatic.liburing.overrideDerivation (old: {
configureFlags = "";
ENABLE_SHARED = 0;
});
# p11-kit cannot be used as a static library
# https://github.com/p11-glue/p11-kit/issues/355
p11-kit = super.pkgsMusl.p11-kit;
# gnutls depends on p11-kit
gnutls = super.pkgsMusl.gnutls;
pam = super.pkgsStatic.openpam;
# support both static and shared
libselinux = (super.pkgsMusl.libselinux.override {
libsepol = super.pkgsStatic.libsepol;
}).overrideAttrs (old: {
makeFlags = old.makeFlags ++ [
"LIBDIR=$(out)/lib"
];
});
glib = (super.pkgsStatic.glib.override {
meson = pkgs.meson;
ninja = pkgs.ninja;
pkg-config = pkgs.pkg-config;
perl = pkgs.perl;
python3 = pkgs.python3;
gettext = pkgs.gettext;
gtk-doc = pkgs.gtk-doc;
docbook_xsl = pkgs.docbook_xsl;
docbook_xml_dtd_45 = pkgs.docbook_xml_dtd_45;
libxml2 = pkgs.libxml2;
}).overrideAttrs (old: {
outputs = super.lib.lists.remove "devdoc" old.outputs;
buildInputs = old.buildInputs ++ [
super.pkgsStatic.libsepol
];
preBuild = ''
sed -i "s/get_option('libmount')/get_option('libmount'), static: true/g" ../meson.build
sed -i "s/get_option('selinux')/get_option('selinux'), static: true/g" ../meson.build
'';
# no devdoc from non-static glibc
# ${pname} & ${version} is correct due to lazy assignment
postInstall = pkgs.glib.postInstall;
});
gtk3 = super.pkgsStatic.gtk3.override {
trackerSupport = false;
cupsSupport = false;
withGtkDoc = false;
# nativeBuildInputs
inherit (pkgs) gettext gobject-introspection makeWrapper meson ninja
pkg-config python3 sassc docbook_xml_dtd_43 docbook-xsl-nons gtk-doc libxml2;
};
qemu = ((super.pkgsStatic.qemu.override {
alsaSupport = false;
spiceSupport = false;
sdlSupport = false;
smartcardSupport = false;
gtkSupport = false;
pulseSupport = false;
# nativeBuildInputs
makeWrapper = pkgs.makeWrapper;
python = pkgs.python3;
pkg-config = pkgs.pkg-config;
flex = pkgs.flex;
bison = pkgs.bison;
meson = pkgs.meson;
ninja = pkgs.ninja;
perl = pkgs.perl;
}).overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs ++ [
pkgs.binutils
# perl as nativeBuildInputs has been added in nixpkgs master
# while it is backported to nixpkgs 21.11 (nixos 21.11).
# If without perl as nativeBuildInputs,
# ./scripts/shaderinclude.pl can not be patchShebangs'ed.
pkgs.perl # without it cannot patchShebangs
];
# qemu-6.1.1 has contained sigrtminmax patch, can not be patched again
patches = builtins.filter (
x: ! super.lib.hasSuffix "sigrtminmax.patch" x
) old.patches;
})).overrideDerivation (old: let
# qemu configure uses "--static" instead of standard "--disable-shared" and "--enable-static"
configureFlags_no_DS = super.lib.lists.remove "--disable-shared" old.configureFlags;
configureFlags_no_DS_no_ES = super.lib.lists.remove "--enable-static" configureFlags_no_DS;
in {
configureFlags = configureFlags_no_DS_no_ES ++ [
"--static"
# "--target-list-exclude="
"--target-list=x86_64-softmmu"
];
});
})];
};
in
{
inherit (mypkgs.pkgsStatic.pkgsCross.riscv64) qemu;
}
#!/usr/bin/env nix-build
# xieby1: 2022.05.24
# build static qemu (current version 6.1.1) into ./result/
# $ nix-build <this-file> -A qemu
# build static qemu-3.1.0 into ./result/
# $ nix-build <this-file> -A qemu31
# build and install static qemu
# $ nix-env -i -f <this-file> -A qemu
# build and install static qemu-3.1.0
# $ nix-env -i -f <this-file> -A qemu31
let
# https://lazamar.co.uk/nix-versions
pinnedPkgsForQemu31Src = builtins.fetchTarball {
name = "nixos-20.03-with-qemu-3.1.0";
url = "https://github.com/NixOS/nixpkgs/archive/81d4e65891f92e8e72c244da663c83c1e40dc919.tar.gz";
sha256 = "0dk1k1zqy2bnp9gsy9mdxk0idkazyvnmqrj2jpbwzfnhjzpmzq1w";
};
pinnedPkgsSrc = builtins.fetchTarball {
name = "nixos-static-qemu";
url = "https://github.com/nixos/nixpkgs/archive/e7d63bd0d50df412f5a1d8acfa3caae75522e347.tar.gz";
sha256 = "132pc4f9ixisyv4117p2jirmlyl6sd76bfaz33rhlcwakg7bhjm7";
};
pkgsForQemu31 = import pinnedPkgsForQemu31Src {};
pkgs = import pinnedPkgsSrc {};
mypkgs = import pinnedPkgsSrc {
overlays = [(self: super: {
cdparanoiaIII = super.pkgsStatic.cdparanoiaIII.overrideAttrs (old: {
preConfigure = old.preConfigure + ''
cp ${super.gnu-config}/config.sub configure.sub
cp ${super.gnu-config}/config.guess configure.guess
'';
# Makefile needs this to compile static
STATIC="TRUE";
buildPhase = ''
make lib
make cdparanoia
'';
preInstallPhases = ["preInstallPhase"];
preInstallPhase = ''
sed -i '/so/d' Makefile
'';
});
liburing = super.pkgsStatic.liburing.overrideDerivation (old: {
configureFlags = "";
ENABLE_SHARED = 0;
});
# p11-kit cannot be used as a static library
# https://github.com/p11-glue/p11-kit/issues/355
p11-kit = super.pkgsMusl.p11-kit;
# gnutls depends on p11-kit
gnutls = super.pkgsMusl.gnutls;
pam = super.pkgsStatic.openpam;
# support both static and shared
libselinux = (super.pkgsMusl.libselinux.override {
libsepol = super.pkgsStatic.libsepol;
}).overrideAttrs (old: {
makeFlags = old.makeFlags ++ [
"LIBDIR=$(out)/lib"
];
});
glib = (super.pkgsStatic.glib.override {
meson = pkgs.meson;
ninja = pkgs.ninja;
pkg-config = pkgs.pkg-config;
perl = pkgs.perl;
python3 = pkgs.python3;
gettext = pkgs.gettext;
gtk-doc = pkgs.gtk-doc;
docbook_xsl = pkgs.docbook_xsl;
docbook_xml_dtd_45 = pkgs.docbook_xml_dtd_45;
libxml2 = pkgs.libxml2;
}).overrideAttrs (old: {
outputs = super.lib.lists.remove "devdoc" old.outputs;
buildInputs = old.buildInputs ++ [
super.pkgsStatic.libsepol
];
preBuild = ''
sed -i "s/get_option('libmount')/get_option('libmount'), static: true/g" ../meson.build
sed -i "s/get_option('selinux')/get_option('selinux'), static: true/g" ../meson.build
'';
# no devdoc from non-static glibc
# ${pname} & ${version} is correct due to lazy assignment
postInstall = pkgs.glib.postInstall;
});
gtk3 = super.pkgsStatic.gtk3.override {
trackerSupport = false;
cupsSupport = false;
withGtkDoc = false;
# nativeBuildInputs
inherit (pkgs) gettext gobject-introspection makeWrapper meson ninja
pkg-config python3 sassc docbook_xml_dtd_43 docbook-xsl-nons gtk-doc libxml2;
};
qemu = ((super.pkgsStatic.qemu.override {
alsaSupport = false;
spiceSupport = false;
sdlSupport = false;
smartcardSupport = false;
gtkSupport = false;
pulseSupport = false;
# nativeBuildInputs
makeWrapper = pkgs.makeWrapper;
python = pkgs.python3;
pkg-config = pkgs.pkg-config;
flex = pkgs.flex;
bison = pkgs.bison;
meson = pkgs.meson;
ninja = pkgs.ninja;
perl = pkgs.perl;
}).overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs ++ [
pkgs.binutils
# perl as nativeBuildInputs has been added in nixpkgs master
# while it is backported to nixpkgs 21.11 (nixos 21.11).
# If without perl as nativeBuildInputs,
# ./scripts/shaderinclude.pl can not be patchShebangs'ed.
pkgs.perl # without it cannot patchShebangs
];
# qemu-6.1.1 has contained sigrtminmax patch, can not be patched again
patches = builtins.filter (
x: ! super.lib.hasSuffix "sigrtminmax.patch" x
) old.patches;
})).overrideDerivation (old: let
# qemu configure uses "--static" instead of standard "--disable-shared" and "--enable-static"
configureFlags_no_DS = super.lib.lists.remove "--disable-shared" old.configureFlags;
configureFlags_no_DS_no_ES = super.lib.lists.remove "--enable-static" configureFlags_no_DS;
in {
configureFlags = configureFlags_no_DS_no_ES ++ [
"--static"
# "--target-list-exclude="
"--target-list=x86_64-softmmu"
];
});
qemu31 = (((super.callPackage (
pinnedPkgsForQemu31Src + "/pkgs/applications/virtualization/qemu/default.nix"
) {
inherit (super.darwin.apple_sdk.frameworks) CoreServices Cocoa Hypervisor;
inherit (super.darwin.stubs) rez setfile;
}).override {
# In nixpkgs 20.03, stdenv contains lib attr.
stdenv = pkgs.pkgsStatic.stdenv // {lib = super.lib;};
alsaLib = null;
spiceSupport = false;
sdlSupport = false;
smartcardSupport = false;
gtkSupport = false;
pulseSupport = false;
# nativeBuildInputs
makeWrapper = pkgs.makeWrapper;
python2 = pkgs.python2;
pkgconfig = pkgs.pkgconfig;
flex = pkgs.flex;
bison = pkgs.bison;
perl = pkgs.perl;
}).overrideAttrs (old: {
nativeBuildInputs = old.nativeBuildInputs ++ [
# Several issues report ld version >= 2.34 will failed due to
# PHDR segment not covered by LOAD segment.
# https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain/issues/122
# https://github.com/genodelabs/genode/issues/4003
# So I downgrade ld version < 2.34.
# I still do not figure out why the same version ld in
# qemu 6.1.1 static works correctly?
pkgsForQemu31.binutils
];
})).overrideDerivation (old: let
# drop audio configure flag
configureFlags_no_audio = builtins.filter (
x: ! super.lib.hasPrefix "--audio-drv-list" x
) old.configureFlags;
# qemu configure uses "--static" instead of standard "--disable-shared" and "--enable-static"
configureFlags_no_DS = super.lib.lists.remove "--disable-shared" configureFlags_no_audio;
configureFlags_no_DS_no_ES = super.lib.lists.remove "--enable-static" configureFlags_no_DS;
in {
configureFlags = configureFlags_no_DS_no_ES ++ [
# "--static"
# "--target-list-exclude="
"--target-list=x86_64-softmmu"
"--disable-gnutls"
"--disable-tools"
];
makeFlags = [
# "V=1" # qemu Makefile verbose
# It is neccessary to use ld in binutils, otherwise pc-bios build will fail.
# In qemu 6.1.1 static, it is using ld in binutils, instead of ld from musl-gcc
"AR=${pkgs.binutils-unwrapped}/bin/ar"
"AS=${pkgs.binutils}/bin/as"
"LD=${pkgs.binutils}/bin/ld"
"NIX_BINTOOLS=${pkgs.binutils}"
];
});
})];
};
in
{
inherit (mypkgs.pkgsStatic) qemu qemu31;
}
#!/usr/bin/env nix-build
# TODO
# Ref: ~/Documents/Tech/BT/QEMU/test.md
let
pkgs = import <nixpkgs> {};
in
pkgs.stdenv.mkDerivation {
buildInputs = with pkgs; [
# configure needs
ninja
pkg-config
glib
# make needs
glibc.static # TODO: this will cause configure failed
];
buildFlags = [
"CFLAGS=-O" # override CFLAGS
"build-tcg-tests-x86_64-linux-user"
];
}
# build by version
# nix-build pkgs_qemu.nix -A v1_0
# build by date
# nix-build pkgs_qemu.nix -A d2011_12_01
{ pkgs ? import <nixpkgs> {} }:
let
pname = "qemu";
in rec {
v1_0 = let
version = "1.0";
in pkgs.gcc49Stdenv.mkDerivation {
inherit pname version;
src = pkgs.fetchurl {
url = "https://download.qemu.org/qemu-${version}.tar.xz";
sha256 = "0y1018xia238pcqb7ad9v299b478c0838fiayl6qkzyd95gk0xbb";
};
buildInputs = with pkgs; [
zlib
pkg-config
glib
python2
];
patches = [
./qemu-v1.0.patch
];
configureFlags = [
"--target-list=x86_64-linux-user"
"--disable-docs"
];
};
d2011_12_01 = v1_0;
}
# https://github.com/NixOS/nixpkgs/issues/185773
{ lib, callPackage, fetchFromGitHub, fetchurl, openssl_1_1 }:
((callPackage (import (fetchFromGitHub {
owner = "NixOS";
repo = "nixpkgs";
rev = "363ef08971726937cd6a63de0efef7f8ba657b18";
sha256 = "sha256-QRKAn5yLMyohZKsK72Vkd6HQUh3t5tDpFyI/Y7T3ASg=";
}) { }).shellinabox.override) { openssl = openssl_1_1; }).overrideAttrs
({ patches, ... }: {
patches = patches ++ [
# OpenSSL 1.1
(fetchurl {
url =
"https://github.com/shellinabox/shellinabox/commit/c32f3d365a0848eb6b3350ec521fcd4d1d098295.patch";
hash = "sha256-Q8otJUip1YQJb0ZSF89BjSvrCh4PQe4R7Rb7mtm33tk=";
})
];
})
See all fhs-shell scripts on [Github repo: scripts/shell]({{ site.repo_url }}/scripts/shell)
let
name = "LinkNan";
pkgs = import <nixpkgs> {};
in pkgs.mkShell {
inherit name;
buildInputs = let
h_content = builtins.toFile "h_content" ''
# ${pkgs.lib.toUpper "${name} usage tips"}
'';
_h_ = pkgs.writeShellScriptBin "h" ''
${pkgs.glow}/bin/glow ${h_content}
'';
mill_0_11_2 = (import (pkgs.fetchFromGitHub {
owner = "NixOS";
repo = "nixpkgs";
rev = "4ab8a3de296914f3b631121e9ce3884f1d34e1e5";
hash = "sha256-mQUik6XZea6ZcCkMpUieq1oxlEDE0vqTTRU9RStgtSQ=";
}){}).mill;
in [
_h_
mill_0_11_2
] ++ (with pkgs; [
xmake
espresso
verilator
# libs
sqlite
zlib
zstd
]);
shellHook = let
circt_1_62_0 = (import (pkgs.fetchFromGitHub {
owner = "NixOS";
repo = "nixpkgs";
rev = "771b079bb84ac2395f3a24a5663ac8d1495c98d3";
sha256 = "0l1l9ms78xd41xg768pkb6xym200zpf4zjbv4kbqbj3z7rzvhpb7";
}){}).circt;
in ''
h
export CHISEL_FIRTOOL_PATH=${circt_1_62_0}/bin/
export NOOP_HOME=$(realpath .)
'';
}
let
pkgs = import <nixpkgs> {};
ccache_dir = toString ./. + "/.ccache";
ccache14Stdenv = pkgs.ccacheStdenv.override {
stdenv = pkgs.gcc14Stdenv;
extraConfig = ''
export CCACHE_COMPRESS=1
export CCACHE_DIR="${ccache_dir}"
export CCACHE_UMASK=007
if [ ! -d "$CCACHE_DIR" ]; then
echo "====="
echo "Directory '$CCACHE_DIR' does not exist"
echo "Please create it with:"
echo " mkdir -m0770 '$CCACHE_DIR'"
echo "====="
exit 1
fi
if [ ! -w "$CCACHE_DIR" ]; then
echo "====="
echo "Directory '$CCACHE_DIR' is not accessible for user $(whoami)"
echo "Please verify its access permissions"
echo "====="
exit 1
fi
'';
};
ccacheMkShell = pkgs.mkShell.override {
stdenv = ccache14Stdenv;
};
in ccacheMkShell {
name = "ccache-shell";
shellHook = ''
mkdir -m0770 -p ${ccache_dir}
'';
}
let
name = "chipyard";
pkgs = import <nixpkgs> {};
pkgsCirct1_30_0 = import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/0aca8f43c8dba4a77aa0c16fb0130237c3da514c.tar.gz";
}) {};
# currently latest spike
my-spike = pkgs.spike.overrideAttrs (old: {
version = "1.1.1-dev";
src = pkgs.fetchFromGitHub {
owner = "riscv";
repo = "riscv-isa-sim";
rev = "4f916978cd17bd2e83cfca233d0fa40153fda5f4";
sha256 = "sha256-84YY9YMIa4YO5mVJ0gGMOWnD2/CnpEjIbB9EjA5+Glc=";
};
});
h_content = builtins.toFile "h_content" ''
# ${pkgs.lib.toUpper "${name} usage tips"}
The conda cannot gracefully manage the dependencies, e.g. gcc's dynamic libraries.
Instead, I replace conda with nix to manage the dependencies.
* Show this help: `h`
Init Repos
* edit common.mk:1 `SHELL=bash`
* `./scripts/init-submodules-no-riscv-tools-nolog.sh`
Compiling Verilator
* `make -C sims/verilator`
Run Verilator
* `./sims/verilator/simulator-chipyard.harness-RocketConfig <RISCV Executable>`
'';
_h_ = pkgs.writeShellScriptBin "h" ''
${pkgs.glow}/bin/glow ${h_content}
'';
in pkgs.mkShell {
inherit name;
packages = with pkgs; [
verilator
dtc
jq
pkgsCirct1_30_0.circt
my-spike
_h_
];
shellHook = ''
export RISCV=${pkgs.pkgsCross.riscv64-embedded.stdenv.cc}
h
'';
}
#!/usr/bin/env -S nix-shell --keep miao
# xieby1: 2022.07.03
# let
# pkgs = import <nixpkgs> {};
# in
# pkgs.mkShell {
# buildInputs = [
# pkgs.pkgsCross.mipsel-linux-gnu.stdenv.cc
# ];
# }
# xieby1: 2022.05.16
let
pkgs_mips_cross = import <nixpkgs> {
crossSystem = "mips-linux";
};
pkgs = import <nixpkgs> {};
in
pkgs.mkShell {
buildInputs = (with pkgs_mips_cross; [
buildPackages.gdb
buildPackages.gcc
]) ++ (with pkgs; [
qemu
]);
}
#!/usr/bin/env -S nix-shell --keep miao
# xieby1: 2022.04.26
let
pkgs_arm_cross = import <nixpkgs> {
# get this config on my android
# nix repl
# builtins.currentSystem
crossSystem = "aarch64-linux";
};
pkgs_arm_native = import <nixpkgs> {
localSystem = "aarch64-linux";
crossSystem = "aarch64-linux";
};
pkgs = import <nixpkgs> {};
in
pkgs.mkShell {
buildInputs = with pkgs_arm_cross; [
# packages for cross compiling, run on local system (x86_64)
stdenv.cc
# here stdenv.cc is the same with buildPackages.gcc
] ++ (with pkgs_arm_native; [
# packages run on aarch64
figlet
]) ++ (with pkgs; [
# packages run on local system (x86_64)
qemu
]);
}
#!/usr/bin/env -S nix-shell --keep miao
{ system ? builtins.currentSystem }:
let
src = fetchTarball {
url = "https://github.com/numtide/devshell/archive/9fddc998b4522694caaf4056e93154d2c11752cd.tar.gz";
sha256 = "0d7ra00843n4iyadhdxcr9m0vcn6fz54hfymms6nbdz0d2pjff06";
};
devshell = import src { inherit system; };
in
devshell.mkShell {
commands = [{
name = "hello";
command = "echo hello";
help = "print hello miao";
}];
}
#!/usr/bin/env -S nix-shell --keep miao
let
pkgs = import <nixpkgs> {};
in pkgs.mkShellNoCC {
name = "gcc11";
packages = with pkgs; [
gcc11
];
}
#!/usr/bin/env -S nix-shell --keep miao
with import <nixpkgs> {};
# You will get a shell with hello executable,
# and environment variable $name, $miao.
mkShell {
packages = [
hello
];
name = "test-env";
miao = "miao!";
}
#!/usr/bin/env -S nix-shell --keep miao
#2022.05.18
# pip install is usable in venv
# e.g.
# $ nix-shell <this_file>
# $ pip install [--user] graphviz2drawio
let
pkgs = import <nixpkgs> {};
in
pkgs.mkShell {
propagatedBuildInputs = with pkgs.python3Packages; [
pip
venvShellHook
ipython
pygame
] ++ (with pkgs; [
]);
venvDir = "pygame";
}
# https://nixos.wiki/wiki/Python
# Python virtual environment
# Execute this commands after entering fhs env
# python -m venv .venv
# source .venv/bin/activate
let
pkgs = import <nixpkgs> {};
in (pkgs.buildFHSUserEnv {
name = "venv";
targetPkgs = p: (with p.python3Packages; [
pip
virtualenv
ipython
]);
}).env
#!/usr/bin/env -S nix-shell --keep miao
let
mach-nix = import (builtins.fetchGit {
url = "https://github.com/DavHau/mach-nix";
ref = "refs/tags/3.4.0";
}) {
pkgs = import <nixpkgs> {};
};
in
mach-nix.mkPythonShell {
requirements = ''
expmcc
'';
}
#!/usr/bin/env -S nix-shell --keep miao
#2022.05.18
# pip install is usable in venv
# e.g.
# $ nix-shell <this_file>
# $ pip install [--user] graphviz2drawio
let
pkgs = import <nixpkgs> {};
in
pkgs.mkShell {
propagatedBuildInputs = with pkgs.python3Packages; [
pip
pygraphviz
venvShellHook
ipython
] ++ (with pkgs; [
graphviz
]);
venvDir = "venv";
}
#!/usr/bin/env -S nix-shell --keep miao
# lxy's qemu plugins: http://172.17.103.58/lixinyu/qemu_plugins
let
pkgs = import <nixpkgs> {};
in pkgs.mkShell {
packages = with pkgs; [
zlib.dev
pkg-config
glib.dev
];
QEMU_DIR = "~/Codes/qemu";
}
#!/usr/bin/env -S nix-shell --keep miao
# --pure: start a pure reproducible shell
# 2022.04.24
# Compile qemu/tests/tcg/x86_64/
# env CFLAGS=-O make -e build-tcg-tests-x86_64-linux-user
{ pkgs ? import <nixpkgs> {} }:
let
name = "nix";
in
pkgs.mkShell {
inherit name;
buildInputs = with pkgs; [
glibc.static
];
inputsFrom = with pkgs; [
qemu
];
}
let
name = "riscv-tests";
pkgs = import <nixpkgs> {};
h_content = builtins.toFile "h_content" ''
# ${pkgs.lib.toUpper "${name} compiling tips"}
* `git submodule update --init --recursive`
* `autoconf`
* `./configure`
* `make -j`
'';
_h_ = pkgs.writeShellScriptBin "h" ''
${pkgs.glow}/bin/glow ${h_content}
'';
in pkgs.mkShell {
inherit name;
packages = with pkgs; [
autoconf
pkgsCross.riscv64-embedded.stdenv.cc
_h_
];
shellHook = ''
export RISCV_PREFIX=${pkgs.pkgsCross.riscv64-embedded.stdenv.cc}/bin/riscv64-none-elf-
h
'';
}
#!/usr/bin/env -S nix-shell --keep miao
with import <nixpkgs> {};
mkShell {
packages = [
dtc
pkgsCross.riscv64-embedded.stdenv.cc
];
name = "spike";
}
#!/usr/bin/env -S nix-shell --keep miao
# --pure: start a pure reproducible shell
{ pkgs ? import <nixpkgs> {}
}:
pkgs.mkShell {
name="dev-environment";
buildInputs = with pkgs; [
texlive.combined.scheme-full # HUGE SIZE!
tmux
];
shellHook = ''
# install texlive permenant
nix-env -q "texlive.*"
if [[ ''$? -ne 0 ]]
then
nix-env -f '<nixpkgs>' -iA texlive.combined.scheme-full
fi
tmux
exit
'';
}
#!/usr/bin/env -S nix-shell --keep miao
{pkgs ? import <nixpkgs> {}}:
let
name = "ucasproposal";
myTexlive = pkgs.texlive.combine {
inherit (pkgs.texlive)
scheme-basic
xetex
ctex
checkcites
# sty
newtx
xstring
realscripts
jknapltx
mathalpha
caption
placeins
enumitem
listings
algpseudocodex
algorithms
algorithmicx
chemfig
mhchem
float
# tex
simplekv
rsfs
;
};
myPython = pkgs.python3.withPackages (p: with p; [
ipython
matplotlib
pandas
numpy
openpyxl
]);
in
pkgs.mkShell {
inherit name;
packages = with pkgs; [
myTexlive
myPython
librsvg
];
shellHook = ''
# env
export PYTHONPATH=${myPython}/${myPython.sitePackages}
export debian_chroot=${name}
'';
}
#!/usr/bin/env -S nix-shell --keep miao
{ pkgsX86 ? import <nixpkgs> {
localSystem.system="aarch64-linux";
crossSystem="aarch64-linux";
} }:
pkgsX86.v8
#!/usr/bin/env -S nix-shell --keep miao
#2022.05.18
# pip install is usable in venv
# e.g.
# $ nix-shell <this_file>
# $ pip install [--user] graphviz2drawio
let
pkgs = import <nixpkgs> {};
in
pkgs.mkShell {
propagatedBuildInputs = with pkgs.python3Packages; [
pip
venvShellHook
ipython
];
venvDir = "${builtins.getEnv "HOME"}/.venv";
}
#!/usr/bin/env -S nix-shell --keep miao
let
pkgs = import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/5e15d5da4abb74f0dd76967044735c70e94c5af1.tar.gz";
sha256 = "0mk86mlxamjxhywdfp5asylqb39z7w18dcy8ds6qvl8gqjrijmq9";
}) {
system = "x86_64-linux";
};
in pkgs.mkShell {
name = "wine6";
packages = with pkgs; [
wine64
];
}
let
name = "xiangshan";
pkgs = import <nixpkgs> {};
in pkgs.mkShell {
inherit name;
buildInputs = let
h_content = builtins.toFile "h_content" ''
# ${pkgs.lib.toUpper "${name} usage tips"}
'';
_h_ = pkgs.writeShellScriptBin "h" ''
${pkgs.glow}/bin/glow ${h_content}
'';
mill_0_11_8 = (import (pkgs.fetchFromGitHub {
owner = "NixOS";
repo = "nixpkgs";
rev = "05bbf675397d5366259409139039af8077d695ce";
sha256 = "1r26vjqmzgphfnby5lkfihz6i3y70hq84bpkwd43qjjvgxkcyki0";
}){}).mill;
in [
_h_
mill_0_11_8
] ++ (with pkgs; [
espresso
verilator
# libs
sqlite
zlib
zstd
]);
shellHook = let
circt_1_62_0 = (import (pkgs.fetchFromGitHub {
owner = "NixOS";
repo = "nixpkgs";
rev = "771b079bb84ac2395f3a24a5663ac8d1495c98d3";
sha256 = "0l1l9ms78xd41xg768pkb6xym200zpf4zjbv4kbqbj3z7rzvhpb7";
}){}).circt;
in ''
h
export CHISEL_FIRTOOL_PATH=${circt_1_62_0}/bin/
export NOOP_HOME=$(realpath .)
'';
}
shell.nix
这个shell.nix用于创建编译我的nix_config仓库的
GitHub Pages的环境。
GitHub Pages的构建主要由markcode
和mdbook
这两个工具支持。
markcode
是我为了方便在源文件里内嵌文档,写的一个小工具。 它能将源文件中带有特殊标记的注释抽取出,成为markdown文件。 nix_config仓库的几乎所有文档都内嵌在.nix文件中, 并都是通过markcode
从.nix文件抽取出来。mdbook
是一个非常纯粹且好用的静态网页生成框架,负责将markdown转换成网页。 因为第一次阅读nix官方文档时就喜欢上了这个文档框架, 所以我也采用的mdbook
作为我的nix_config的文档框架。
let
name = "nix_config";
pkgs = import <nixpkgs> {};
markcode = pkgs.callPackage (
pkgs.fetchFromGitHub {
owner = "xieby1";
repo = "markcode";
rev = "1c414aca28db7f2727f6da118f4e914743780ad0";
hash = "sha256-B5kmpAIyUihlBqk7oNAdqBmdfCajCmleKBTgLyy0NqU=";
}
) {};
in pkgs.mkShell {
inherit name;
buildInputs = with pkgs; [
mdbook
markcode
];
}