🏗️ 我的Nix/NixOS配置详细文档正在施工中,完成进度:44/107 🏗️

为了更好的文档阅读体验,请看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稳定源(目前版本24.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/: 独立的软件包脚本
  • system.nix: 系统总体配置(nixos-rebuild的配置)
    • sys/cli.nix: 系统命令行配置
    • sys/gui.nix: 系统图形配置
    • modules/: 系统模块
  • home.nix: 用户总体配置(home-manager的配置)
    • usr/cli.nix: 用户命令行配置
    • usr/gui.nix: 用户图形配置
    • 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中添加system.nix的路径
vim /etc/nixos/configuration.nix

然后设置软件源,在NixOS中推荐使用sudo

  • 注一:更多其他nix channels参考 NixOS Wiki: Nix channelsNix channel status
  • 注二:为什么用https://nixos.org/channels/nixos-24.05, 而非https://github.com/NixOS/nixpkgs/archive/release-24.05.tar.gz? 前者包含额外内容,比如programs.command-not-found.dbPath,详细见man configuration.nix
# [对于NixOS]
nix-channel --add https://nixos.org/channels/nixos-24.05 nixos
# [对于Nix]
nix-channel --add https://nixos.org/channels/nixos-24.05 nixpkgs
# 添加home manager源
nix-channel --add https://github.com/nix-community/home-manager/archive/release-24.05.tar.gz home-manager
nix-channel --update

最后部署配置

# [仅NixOS]
sudo nixos-rebuild switch
# 安装home-manager
nix-shell '<home-manager>' -A install
home-manager switch

引用

1

nix 2.8的impure特性和home-manager等会引入一些你几乎察觉不到的差异。

2

Dolstra, Eelco. “The purely functional software deployment model.” (2006).

3

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

nix语言中只有let ... in来定义局部变量,没有提供全局变量的语法支持。 但在我的nix配置中又很需要一些“全局变量”来方便统一的管理多个配置文件。 有2种可行的方案来实现全局变量:module和import。

module

nixpkgs的module能够还不错地实现“全局变量”。 想了解module?可以去看看NixOS wikiL modules。 或者看看nixpkgs源码关于modules.nix的部分<nixpkgs>/lib/modules.nix

要注意的是,基于module的“全局变量”也会有局限的。 module是用过imports变量导入的,若在imports语句访问“全局变量”,nix的lazy evaluation的特性会导致死循环。 这也是为什么老版本的home.nix中判断是否需要导入./usr/gui.nix, 我没有使用config.isGui而是再次使用getEnv访问环境变量。 当然这个不足也是我放弃使用的module主要原因。

import

通过在配置文件中import opt.nix也能还不错的实现“全局变量”。 import方法的最大优势是能够在module imports语句中避免死循环。

这个方法的不足之处在于每个需要使用全局变量的文件都需要import opt.nix。 但这个不方便之处,相对imports死循环,没那么让人难受。

{
  isMinimalConfig = false;

proxyPort:代理端口号,诸多网络程序需要用,比如clash和tailscale。

  proxyPort = 8889;

isCliisGui:通过环境变量DISPLAY来判断是否是CLI或GUI环境。 这个方法有局限,比如ssh连接到一台有GUI的电脑上,ssh里是没有设置环境变量DISPLAY的。 因此更好的方法是在opt-local.nix中写入固定的isCli和isGui值。

  isCli = (builtins.getEnv "DISPLAY")=="";
  isGui = (builtins.getEnv "DISPLAY")!="";

isNixOnDroid:通过用户名来判断是否是nix-on-droid。

  isNixOnDroid = (builtins.getEnv "USER")=="nix-on-droid";

isWSL2:通过环境变量WSL_DISTRO_NAME来判断是否是WSL2。

  isWSL2 = (builtins.getEnv "WSL_DISTRO_NAME")!="";
}

本地配置

引入opt-local.nix,方便本地进行自定义,避免我的nix配置中出现过多设备特定代码(即一堆if else判断语句)。 为了减少设备特定代码,每个设备都有自己的opt-local.nix。 目前的想法是不将opt-local.nix加入git仓库,以实现“设备特定”。 相比opt.nix,opt-local.nix的配置具备更高的优先级。

// (
  if (builtins.pathExists ./opt-local.nix)
  then import ./opt-local.nix
  else {}
)

system.nix

本文件system.nix存放喆我的NixOS的配置。 NixOS的配置通过命令sudo nixos-rebuid switch生效。 因为NixOS的配置入口是/etc/nixos/configuration.nix, 所以需要在/etc/nixos/configuration.nix中import本文件,例如

# /etc/nixos/configuration.nix:
{ config, pkgs, ... }: {
  imports = [
    ./hardware-configuration.nix
    # import my system.nix here!
    /home/xieby1/.config/nixpkgs/system.nix
  ];
  # 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配置, 详细内容见文档./sys/cli.nix./sys/gui.nix

  imports = [
    ./sys/modules/cachix.nix
    ./sys/cli.nix
    ./sys/gui.nix
  ];

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用户服务的配置,详细可见参考:

}

cli.nix

本文件是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;

系统配置

给jumper电脑自动挂在SD卡。

  # refers to https://www.golinuxcloud.com/automount-file-system-systemd-rhel-centos-7-8/
  systemd.mounts = if "${config.networking.hostName}" == "jumper"
  then [{
    enable = true;
    # [Unit]
    description = "My SD Card";
    unitConfig = {
      DefaultDependencies = "no";
      Conflicts = "umount.target";
    };
    before = ["local-fs.target" "umount.target"];
    after = ["swap.target"];
    # [Mount]
    what = "/dev/disk/by-label/home";
    where = "/home";
    type = "ext4";
    options = "defaults";
    # [Install]
    wantedBy = ["multi-user.target"];
  }] else [];

  systemd.automounts = if "${config.networking.hostName}" == "jumper"
  then [{
    enable = true;
    # [Unit]
    description = "automount sdcard";
    # [Automount]
    where = "/home";
    # [Install]
    wantedBy = ["multi-user.target"];
  }] else [];

启用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 = let
    version = "9.0.2+ds-6";
    qemu-user-static = pkgs.nur.repos.xddxdd.qemu-user-static.override {
      sources = {
        qemu-user-static-amd64 = {
          pname = "qemu-user-static-amd64";
          inherit version;
          src = builtins.fetchurl {
            url = "http://ftp.debian.org/debian/pool/main/q/qemu/qemu-user-static_${version}_amd64.deb";
            sha256 = "15gfs9011smppbank3vzla09yrq84xrhbcn3zwqjnl9xvkzsp2bw";
          };
        };
        qemu-user-static-arm64 = {
          pname = "qemu-user-static-arm64";
          inherit version;
          src = builtins.fetchurl {
            url = "http://ftp.debian.org/debian/pool/main/q/qemu/qemu-user-static_${version}_arm64.deb";
            sha256 = "1as8s74dmmqp2sv2msblb2vngn1y3611ix9yjq012dlgck2h77id";
          };
        };
      };
    };
  in pkgs.lib.filterAttrs (n: v: n!=builtins.currentSystem) {
    x86_64-linux = {
      interpreter = "${qemu-user-static}/bin/qemu-x86_64-static";
      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 = "${qemu-user-static}/bin/qemu-aarch64-static";
      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 = "${qemu-user-static}/bin/qemu-riscv64-static";
      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;

  documentation.man.generateCaches = true;
}
{ 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: {
      gnome = prev.gnome.overrideScope (gfinal: gprev: {
        mutter = let
          mutter-x11-scaling = pkgs.fetchFromGitHub {
            owner = "puxplaying";
            repo = "mutter-x11-scaling";
            rev = "8c5d5224955a5ec6a36697e9b64e606e6a596ef7";
            hash = "sha256-TDQkDgAcuNnzSqiYE364E/Z2mBKegevVQ3AeOYBjHc4=";
          };
        in gprev.mutter.overrideAttrs (old: {
          patches = (pkgs.lib.optionals (old ? patches) old.patches) ++ [
            "${mutter-x11-scaling}/mutter-fix-x11-restart.patch"
            "${mutter-x11-scaling}/x11-Add-support-for-fractional-scaling-using-Randr.patch"
            "${mutter-x11-scaling}/Support-Dynamic-triple-double-buffering.patch"
          ];
        });
        gnome-control-center = let
          gnome-control-center-x11-scaling = pkgs.fetchFromGitHub {
            owner = "puxplaying";
            repo = "gnome-control-center-x11-scaling";
            rev = "9d4b878a523151776cea0fad47a0421c660ea8af";
            hash = "sha256-xAf0jXs9holjN5xa20d4VLesbwitNq0w+9p5qyw7Ut8=";
          };
        in gprev.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.gnome; [mutter gnome-control-center];
  }] ;

  # Enable the X11 windowing system.
  services.xserver.enable = true;
  services.xserver.videoDrivers = [
    "nvidia"
    # default video drivers
    "radeon" "nouveau" "modesetting" "fbdev"
    "intel" "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.enabled = "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 (import (fetchTarball {
      url = "https://github.com/NixOS/nixpkgs/archive/d881cf9fd64218a99a64a8bdae1272c3f94daea7.tar.gz";
      sha256 = "1jaghsmsc05lvfzaq4qcy281rhq3jlx75q5x2600984kx1amwaal";
    }) {}); [
    noto-fonts-cjk-sans
    noto-fonts-cjk-serif
    noto-fonts-emoji]) ++ (with pkgs; [
    (nerdfonts.override {
      # The best developer fonts, see https://www.nerdfonts.com/
      fonts = [
        "Hack"
        "Meslo"
        "SourceCodePro"
        "FiraCode"
        "Terminus"
        "Iosevka"
        "Monoid"
        "FantasqueSansMono"
      ];
    })
    # refs to pkgs/data/fonts/roboto-mono/default.nix
    # (stdenv.mkDerivation {
    #   name = "my_fonts";
    #   srcs = [(fetchurl {
    #     url = "https://github.com/lxgw/LxgwWenKai/releases/download/v1.311/LXGWWenKai-Bold.ttf";
    #     sha256 = "16111vvjii2hmnigjb44rjj39k8hjawbvwrb3f2f1ph4hv5wnvkn";
    #   }) (fetchurl {
    #     url = "https://github.com/lxgw/LxgwWenKai/releases/download/v1.311/LXGWWenKai-Regular.ttf";
    #     sha256 = "103mvbpg51jvda265f29sjq17jj76dgwz6f1qdmv6d99bb8b6x7w";
    #   })];
    #   sourceRoot = "./";
    #   unpackCmd = ''
    #     ttfName=$(basename $(stripHash $curSrc))
    #     cp $curSrc ./$ttfName
    #   '';
    #   installPhase = ''
    #     mkdir -p $out/share/fonts/truetype
    #     cp -a *.ttf $out/share/fonts/truetype/
    #   '';
    # })
  ]);
  # 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;

  programs.evolution = {
    enable = true;
    plugins = [pkgs.evolution-ews];
  };

}
{ 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.

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

home.nix是home-manager的入口配置文件,它导入了3个nix文件:

因为部分系统不需要GUI程序,比如安卓手机nix-on-droid或是树莓派等。 我利用环境变量DISPLAY用于判断是否导入GUI程序的配置。

{ config, pkgs, stdenv, lib, ... }:
let
  opt = import ./opt.nix;
in {
  imports = [
    ./usr/modules/cachix.nix
    ./usr/cli.nix
  ] ++ lib.optionals opt.isGui [
    ./usr/gui.nix
  ];

  home.stateVersion = "19.09";
  programs.home-manager.enable = true;
}

cli-extra.nix: Extra CLI configs (added to minial cli.nix)

{ config, pkgs, stdenv, lib, ... }:
let
  opt = import ../opt.nix;
in {
  imports = [{
    home.packages = let
      pandora = pkgs.python3Packages.callPackage ./cli/pandora-chatgpt.nix {};
      chatgpt = pkgs.writeShellScriptBin "chatgpt" ''
        ${pandora}/bin/pandora -t ~/Gist/Pandora-ChatGPT/access_token.dat $@
      '';
    in [pandora chatgpt];
  }{
    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 opt.proxyPort}'"
          "--env" "HTTP_PROXY='http://127.0.0.1:${toString opt.proxyPort}'"
          "--env" "FTP_PROXY='http://127.0.0.1:${toString opt.proxyPort}'"
          "--env" "https_proxy='http://127.0.0.1:${toString opt.proxyPort}'"
          "--env" "http_proxy='http://127.0.0.1:${toString opt.proxyPort}'"
          "--env" "ftp_proxy='http://127.0.0.1:${toString opt.proxyPort}'"

          "$@"
        )
        eval "''${CMD[@]}"
      '')
    ];
  }];

  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;
}
{ config, pkgs, stdenv, lib, ... }:
let
  opt = import ../opt.nix;
  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
    '';
  };
  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 = lib.optionals (!opt.isMinimalConfig) [
    ./cli-extra.nix
  ] ++ [ # files
    ./cli/vim
    ./cli/tmux.nix
    ./cli/clash.nix
    ./cli/tailscale.nix
    # When init searxng, it throws `ERROR:searx.engines.wikidata: Fail to initialize`
    # I have no idea, so disable it.
    # ./cli/searxng.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 (!opt.isNixOnDroid) pkgs.hstr;
    programs.bash.bashrcExtra = lib.optionalString (!opt.isNixOnDroid) ''
      # HSTR configuration - add this to ~/.bashrc
      alias hh=hstr                    # hh to be alias for hstr
      export HSTR_CONFIG=hicolor       # get more colors
      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
    '';
  }{
    programs.ssh.enable = true;
    programs.ssh.includes = lib.optional (builtins.pathExists ~/Gist/Config/ssh.conf) "~/Gist/Config/ssh.conf";
    programs.bash.bashrcExtra = lib.optionalString opt.isNixOnDroid ''
      # start sshd
      if [[ -z "$(pidof sshd-start)" ]]; then
          tmux new -d -s sshd-start sshd-start
      fi
    '';
  }{
    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;
    };
    home.file.mr = {
      text = if builtins.pathExists ~/Gist/Config/mrconfig
        then builtins.readFile ~/Gist/Config/mrconfig
        else "";
      target = ".mrconfig";
    };
    # mr status not work in non-home dir
    programs.bash.shellAliases.mr = "mr -d ~";
  }{
    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 opt.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 opt.proxyPort}"
    ];

使用命令行浏览器browsh来实现syncthing-tui。

    home.packages = lib.optional (!opt.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; [
      universal-ctags
    ];
    home.file.exclude_ctags = {
      text = ''
        # exclude ccls generated directories
        --exclude=.ccls*
      '';
      target = ".config/ctags/exclude.ctags";
    };
  }];

  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
    ## manual
    tldr
    ## file system
    file
    # magika # detect file content types with deep learning
    tree
    ## network
    wget
    bind.dnsutils # nslookup
    netcat
    nload
    nmap
    nethogs
    ## x11
    xdotool

    # programming
    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
    ]))
    ## 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
    )
    gdb
    ### 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 (!opt.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 opt.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")"'
  '';
  ## 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))

      _notification_threthold=10
      if [[ $last_timer -ge $_notification_threthold ]]; then
        _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
      fi
    }
    function timer_unset {
      unset _timer
    }

    trap timer_start DEBUG

    PROMPT_COMMAND="timer_stop;$PROMPT_COMMAND"
    if [[ -n "$(echo $PROMPT_COMMAND | grep -o -e ';$')" ]]; then
      PROMPT_COMMAND+="timer_unset;"
    else
      PROMPT_COMMAND+=";timer_unset;"
    fi
  '';

  home.file.gdbinit = {
    source = pkgs.fetchurl {
      url = "https://raw.githubusercontent.com/cyrus-and/gdb-dashboard/2b107b27949d13f6ef041de6eec1ad2e5f7b4cbf/.gdbinit";
      sha256 = "02rxyk8hmk7xk1pyhnc5z6a2kqyd63703rymy9rfmypn6057i4sr";
      name = "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";
  };

  programs.direnv.enable = true;
  programs.direnv.nix-direnv.enable = true;

  programs.man.generateCaches = true;
}
{ config, pkgs, stdenv, lib, ... }:
let
  opt = import ../../opt.nix;
  clashctl = pkgs.callPackage ./clashctl.nix {};
in {
  imports = [{
    home.packages = [
      pkgs.clash-meta
    ] ++ lib.optional (!opt.isNixOnDroid) clashctl;
    cachix_packages = lib.optional (!opt.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 (!opt.isNixOnDroid) ''
      # proxy
      ## default
      HTTP_PROXY="http://127.0.0.1:${toString opt.proxyPort}/"
      ## microsoft wsl
      # if [[ $(uname -r) == *"microsoft"* ]]; then
      #     hostip=$(cat /etc/resolv.conf | grep nameserver | awk '{ print $2 }')
      #     export HTTP_PROXY="http://$hostip:${toString opt.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-2d+phmMJeHZsJy300GwUAnh8KTGzpHCCIg8x4Wr8ytE=";

  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 ];
  };
}
{ 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 and pkgs/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"

2023.05.03

打包python(pip)包,以pandora-chatgpt为例

太常不看

  • nix文件: https://github.com/xieby1/nix_config/blob/main/usr/cli/pandora-chatgpt.nix
  • 使用方法:
    pkgs.python3Packages.callPackage ./cli/pandora-chatgpt.nix {};
    
  • Add python optional dependencies (extras): see nixpkgs manual 17.27.2.3.2. Optional extra dependencies
{ buildPythonPackage
, fetchPypi
, appdirs
, certifi
, flask
, flask-cors
, httpx
, loguru
, pyjwt
, pyperclip
, requests
, rich
, sentry-sdk
, waitress
, werkzeug
}:

buildPythonPackage rec {
  pname = "Pandora-ChatGPT";
  version = "1.2.5";
  src = fetchPypi {
    inherit pname version;
    hash = "sha256-tfGqBRf/0VyaBuXsZEH8LwAuJYfk7oimY5Y0M1d/Qxs=";
  };
  doCheck = false;
  preBuild = ''
    # ignore version
    sed -i 's/\~=[^;]*//g' requirements.txt
  '';
  propagatedBuildInputs = [
    appdirs
    certifi
    flask
    flask-cors
    httpx
  ] ++ httpx.optional-dependencies.socks ++ [
    loguru
    pyjwt
  ] ++ pyjwt.optional-dependencies.crypto ++ [
    pyperclip
    requests
  ] ++ requests.optional-dependencies.socks ++ [
    rich
    sentry-sdk
    waitress
    werkzeug
    flask
  ] ++ flask.optional-dependencies.async;
}

Python in Nix/NixOS

2023.05.03
ProjectMaintainedDescriptionPurityCompatibility
venv100%?
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

2022.05.15

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, pkgs, stdenv, lib, ... }:
let
  opt = import ../../opt.nix;
  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 opt.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";
    };
  };
}
2022.05.29

静态链接,以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"

TODO: 读懂makeStaticLibraries和makeStaticBinaries。

基本思路

对于当前版本的qemu,使用pkgsStatic.qemu ,挨个修复编译报错即可。

对于历史版本的qemu,以3.1.0为例,编译方式已和现在有区别, 比如之前完全是configure, make,而现在引入了meson。 初步设想两种方案,

  1. 嫁接老版本nixpkgs到当前版本 将qemu-3.1.0对应的nixpkgs嫁接到当前的nixpkgs
  2. 改造新版本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
  opt = import ../../opt.nix;
  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 opt.proxyPort}"
          "HTTP_PROXY=http://127.0.0.1:${toString opt.proxyPort}"
          "https_proxy=http://127.0.0.1:${toString opt.proxyPort}"
          "http_proxy=http://127.0.0.1:${toString opt.proxyPort}"
        ];
        ExecStart = "${tailscaled-wrapped}/bin/tailscaled-${suffix}";
      };
    };
    programs.bash.bashrcExtra = lib.optionalString opt.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, ... }:
let
  opt = import ../../opt.nix;
in {
  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 (!opt.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 "#I #W #{=/-20/…:pane_title}"
      set -g window-status-current-format "🐶#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

DrawIt: ascii art drawing

{ 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";
    };
  };
in {
  programs.neovim = {
    plugins = [
      DrawIt
    ];
  };
}

🎨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

TODO: replace my conform-nvim with nixpkgs's one.

{ config, pkgs, stdenv, lib, ... }:
let
  my-conform-nvim = let
    nvim_doc_tools = pkgs.fetchFromGitHub {
      owner = "stevearc";
      repo = "nvim_doc_tools";
      rev = "2fe4503c704ac816efdbbbf70d0c070ed3052bba";
      sha256 = "sha256-PtUMBHB+1IV8Q2L6pYKKvx7bliq63Z+2v8IDC5mvAeo=";
    };
    nvim-typecheck-action = pkgs.fetchFromGitHub {
      owner = "stevearc";
      repo = "nvim-typecheck-action";
      rev = "0a5ddc13b800c50bac699edd443f494a089824cd";
      sha256 = "sha256-Mzzt2A0WCeaeIDiCWgHii+RUQNlQssPxK5/LVaEgpbU=";
    };
    neodev_nvim = pkgs.fetchFromGitHub {
      owner = "folke";
      repo = "neodev.nvim";
      rev = "627b5b543f4df551fcddb99c17a8e260c453400d";
      sha256 = "sha256-S8/dUOcVPUeh54ZTELql/H5nW3DghpCtWzyxaPjZbCw=";
    };
  in {
    plugin = pkgs.vimUtils.buildVimPlugin {
      name = "conform.nvim";
      src = pkgs.fetchFromGitHub {
        owner = "stevearc";
        repo = "conform.nvim";
        rev = "a36c68d2cd551e49883ddb2492c178d915567f58";
        sha256 = "sha256-aul/6sQZMljF3nc+WrRhVEObytu4wkoVyTM5HognK7E=";
      };
      buildInputs = with pkgs; [
        python3Packages.pyparsing
        python3
        luajitPackages.luacheck
        stylua
        git
        lua-language-server
      ];
      preBuild = ''
        sed -i 's/all: doc lint test/all: doc/g' Makefile

        mkdir -p scripts/nvim_doc_tools
        cp -r ${nvim_doc_tools}/* scripts/nvim_doc_tools/
        # disable ShaDa
        sed -i 's/nvim --headless/nvim --headless -i NONE/' scripts/nvim_doc_tools/util.py

        mkdir -p scripts/nvim-typecheck-action
        cp -r ${nvim-typecheck-action}/* scripts/nvim-typecheck-action/
        patchShebangs scripts/nvim-typecheck-action/

        mkdir -p scripts/nvim-typecheck-action/libs/neodev.nvim
        cp -r ${neodev_nvim}/* scripts/nvim-typecheck-action/libs/neodev.nvim/
      '';
    };
    type = "lua";
    config = ''
      require("conform").setup({
        formatters_by_ft = {
          nix = { "nixfmt" },
          c = { "clang_format" },
          cpp = { "clang_format" },
        },
      })

      -- 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>')
    '';
  };
in {
  programs.neovim = {
    plugins = [
      my-conform-nvim
    ];
    extraPackages = with pkgs; [
      nixfmt-rfc-style
      clang-tools
    ];
  };
}
{ config, pkgs, stdenv, lib, ... }:
let
  opt = import ../../../opt.nix;
in {

Plugins with customizations:

  imports = [
    ./nvim-metals
    ./conform-nvim.nix
    ./nvim-lspconfig.nix
    ./nvim-cmp.nix
    ./vim-mark.nix
    ./DrawIt.nix
    ./nvim-treesitter.nix
    ./nvim-config-local.nix
    ./leap-nvim.nix
    ./telescope-nvim.nix
    ./git-wip.nix
    ./vim-floaterm.nix
    ./vista-vim.nix
    ./vim-markdown.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
  ];

  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
      tabular
      vim-plugin-AnsiEsc
    ] ++ (lib.optional opt.isGui markdown-preview-nvim);

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 4 spaces width
      set tabstop=4
      """ when indenting with '>', use 4 spaces width
      set shiftwidth=4
      """ specicial indent
      au FileType markdown setlocal shiftwidth=2 tabstop=2
      """ On pressing tab, insert 4 spaces
      set expandtab
      """ line wrap with ident
      set breakindent
      """" horizontally scroll 4 characters
      nnoremap z<left> 4zh
      nnoremap z<right> 4zl
      "" tags support, ';' means upward search, refering to http://vimdoc.sourceforge.net/htmldoc/editing.html#file-searching
      set tags=./tags;
      "" 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
      """ mouse support " select by pressing shift key!
      set mouse=a
      """ matchit.vim " :h matchit-install
      packadd! matchit
      "" 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
      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>"
    '';
  };
}

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 {
        signcolumn = false,
        numhl      = true,
        linehl     = true,

        current_line_blame = true,

        -- 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=";
      };
    };
    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').create_default_mappings()
    '';
  };
in {
  programs.neovim = {
    plugins = [
      my-leap-nvim
    ];
  };
}

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()
    '';
  };
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: load local vim config

# TODO: use native neovim local local config ability.
{ config, pkgs, stdenv, lib, ... }:
let
  my-nvim-config-local = {
    plugin = pkgs.vimUtils.buildVimPlugin {
      name = "nvim-config-local";
      src = pkgs.fetchFromGitHub {
        owner = "klen";
        repo = "nvim-config-local";
        rev = "af59d6344e555917209f7304709bbff7cea9b5cc";
        sha256 = "1wg6g4rqpj12sjj0g1qxqgcpkzr7x82lk90lf6qczim97r3lj9hy";
      };
    };
    config = ''
      lua << EOF
      require('config-local').setup {
        lookup_parents = true,
        silent = true,
      }
      EOF
      augroup config-local
        autocmd BufEnter * nested lua require'config-local'.source()
      augroup END
    '';
  };
in {
  programs.neovim = {
    plugins = [
      my-nvim-config-local
    ];
  };
}

nvim-lspconfig

{ config, pkgs, stdenv, lib, ... }:
let
  my-nvim-lspconfig = {
    plugin = pkgs.vimPlugins.nvim-lspconfig;
    type = "lua";
    config =

Global mappings.

    ''
      -- 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,
      })
    ''

Text

    + ''
      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,
            },
          },
        },
      }
    ''

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?

GitHub: ccls: issue: I'm very confused about this question, it's about ccls or neovim built in LSP? #836

No one try to fix the two-replies problem in ccls. However, nimaipatel recommanded clangd_extensions.

    + ''
      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()
    ''

Nix

    + ''
      require('lspconfig').nixd.setup{}
    ''

Html

    + ''
      require('lspconfig').html.setup{}
    ''

Python

    + ''
      require('lspconfig').pyright.setup{}
    ''

Lua

    + ''
      require('lspconfig').lua_ls.setup{}
    ''

Auto completion

    + ''
      -- Add additional capabilities supported by nvim-cmp
      local capabilities = require("cmp_nvim_lsp").default_capabilities()

      -- Enable some language servers with the additional completion capabilities offered by nvim-cmp
      local servers = {
        'ltex',
        'clangd',
        'nixd',
        'html',
        'pyright',
        'lua_ls',
      }
      for _, lsp in ipairs(servers) do
        require('lspconfig')[lsp].setup {
          -- on_attach = my_custom_on_attach,
          capabilities = capabilities,
        }
      end
    '';
  };

Setting up my nvim-lspconfig

in {
  programs.neovim = {
    plugins = [
      my-nvim-lspconfig
      pkgs.vimPlugins.clangd_extensions-nvim
    ];
    extraPackages = with pkgs; [
      clang-tools
      ltex-ls
      nixd
      vscode-langservers-extracted # html
      pyright
      lua-language-server
    ];
  };
}

nvim-metals: Scala LSP

The JRE proxy need subtle configuration.

{ config, pkgs, ... }:
let
  opt = import ../../../../opt.nix;
  jre_with_proxy = pkgs.callPackage ./jre_with_proxy.nix {
    jre = pkgs.openjdk_headless;
    proxyHost = "127.0.0.1";
    proxyPort = toString opt.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 opt.proxyPort}",
          "-Dhttp.proxyHost=127.0.0.1",
          "-Dhttp.proxyPort=${toString opt.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 = ture,
      }
      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 opt.proxyPort}
      -Dhttp.proxyHost=127.0.0.1
      -Dhttp.proxyPort=${toString opt.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

{ stdenv
, writeShellScript
, jre
, 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 builtins.derivation rec {
  name = "jre_with_proxy";
  system = builtins.currentSystem;
  builder = writeShellScript "${name}-builder" ''
    source ${stdenv}/setup

    mkdir -p $out
    for dir in ${jre}/*; do
      ln -s $dir $out/
    done

    rm $out/bin
    mkdir -p $out/bin
    for file in ${jre}/lib/openjdk/bin/*; do
      ln -s $file $out/bin/
    done

    rm $out/bin/java
    ln -s ${java_with_proxy_sh} $out/bin/java
  '';
}

nvim-treesitter: languages parsing

{ config, pkgs, stdenv, lib, ... }:
let
  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 (p: with p; [
      c
      cpp
      python
      markdown
      lua
    ]);
    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
    ];
  };
}

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 fg <cmd>lua require('telescope.builtin').live_grep({cwd=require'telescope.utils'.buffer_dir()})<cr>
      nnoremap fF <cmd>lua require('telescope.builtin').find_files()<cr>
      nnoremap fG <cmd>lua require('telescope.builtin').live_grep()<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>
    '';
  };
  my-telescope-fzf-native-nvim = {
    plugin = pkgs.vimPlugins.telescope-fzf-native-nvim;
    type = "lua";
    config = ''
      require('telescope').setup {
        extensions = {fzf = {}},
        defaults = {
          layout_strategy = 'vertical'
        }
      }
      require('telescope').load_extension('fzf')
    '';
  };
in {
  programs.neovim = {
    plugins = [
      my-telescope-nvim
      my-telescope-fzf-native-nvim
      pkgs.vimPlugins.plenary-nvim
    ];
    extraPackages = with pkgs; [
      ripgrep
    ];
  };
}

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
      \   },
      \ }
    '';
  };
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.

vccvcc tgcvh 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 = "8ea0e934d725a0339f16375f248fbf1235ced5f6";
      sha256 = "1rabyhayxswwh85lp4rzi2w1x1zbp5j0v025vsknzbqi0lqy32nk";
    };
  };
  my-vim-mark = {
    plugin = pkgs.vimUtils.buildVimPlugin {
      name = "vim-mark";
      src = pkgs.fetchFromGitHub {
        owner = "inkarkat";
        repo = "vim-mark";
        rev = "7f90d80d0d7a0b3696f4dfa0f8639bba4906d037";
        sha256 = "0n8r0ks58ixqv7y1afliabiqwi55nxsslwls7hni4583w1v1bbly";
      };
    };
    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
    ];
  };
}

vim-markdown: md support

我主要使用这个插件的对齐表格的功能,:TableFormat,超酷炫!

{ config, pkgs, stdenv, lib, ... }:
let
  my-vim-markdown = {
    plugin = pkgs.vimPlugins.vim-markdown;
    config = ''
      let g:vim_markdown_new_list_item_indent = 2
      let g:vim_markdown_no_default_key_mappings = 1
    '';
  };
in {
  programs.neovim = {
    plugins = [
      my-vim-markdown # format table
    ];
  };
}

vista-vim: lsp symbols

{ config, pkgs, stdenv, lib, ... }:
let
  my-vista-vim = {
    plugin = pkgs.vimPlugins.vista-vim;
    config = ''
      let g:vista_default_executive = 'nvim_lsp'
    '';
  };
in {
  programs.neovim = {
    plugins = [
      my-vista-vim
    ];
  };
}

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
    ];
  };
}
{ config, pkgs, stdenv, lib, ... }:
let
  opt = import ../opt.nix;
  xelfviewer = pkgs.callPackage ./gui/xelfviewer.nix {};
in
{
  imports = [
    ./gui/mime.nix
    ./gui/kdeconnect.nix
    ./gui/xdot.nix
    ./gui/firefox.nix
  ] ++ (lib.optionals (builtins.currentSystem=="x86_64-linux") [
    ./gui/rustdesk.nix
  ]) ++ (if !opt.isWSL2 then [
    ./gui/gnome.nix
    ./gui/terminal.nix
    ./gui/singleton_web_apps.nix
    ./gui/rofi.nix
  ] else [{ # install fonts for WSL
    fonts.fontconfig.enable = true;
    home.packages = with pkgs; [
      noto-fonts-cjk-sans
      noto-fonts-cjk-serif
      noto-fonts-emoji
    ];
  }]);

  home.packages = with pkgs; [
    libnotify
    # browser
  ] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
    google-chrome
    microsoft-edge
  ] ++ [
    # 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"
        ];
      });
      uosLicense = fetchurl {
        url = "https://github.com/xddxdd/nur-packages/raw/master/pkgs/uncategorized/wechat-uos/license.tar.gz";
        sha256 = "0sdx5mdybx4y489dhhc8505mjfajscggxvymlcpqzdd5q5wh0xjk";
      };
    })
    # wine weixin waste too much memory, more than 4GB!!!
    #(import ./gui/weixin.nix {})
    nur.repos.linyinfeng.wemeet
    nur.repos.xddxdd.dingtalk
    # telegram desktop not provide aarch64 prebuilt
    tdesktop
  ] ++ [
    transmission-gtk
    # text
    #wpsoffice
    libreoffice
    meld
    # TODO: use this after switching to wayland
    #wl-clipboard
    textsnatcher
    # draw
    drawio
    #aseprite-unfree
    inkscape
    gimp
    # viewer
  ] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
    imhex
    xelfviewer
  ] ++ [
    vlc
  ] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
    ghidra
  ] ++ [
    # management
  ] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
    zotero
  ] ++ [
    barrier
    keepassxc
    # entertainment
    antimicrox
    # music
  ] ++ pkgs.lib.optionals (builtins.currentSystem=="x86_64-linux") [
    spotify
  ];

  xdg.mime.types = {
    drawio = {
      name = "draw-io";
      type = "text/draw-io";
      pattern = "*.drawio";
      defaultApp = "drawio.desktop";
    };
  };

  home.file.autostart_barrier = {
    source = "${pkgs.barrier}/share/applications/barrier.desktop";
    target = ".config/autostart/barrier.desktop";
  };
}
2022.05.11

安装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 {}; callPackage ./default.nix {}"

权限问题

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 // 负责图标

{ config, pkgs, stdenv, lib, ... }:
{
  programs.firefox = {
    enable = true;
    # If state version ≥ 19.09 then this should be a wrapped Firefox
    package = pkgs.firefox.overrideAttrs (old: {
      desktopItem = old.desktopItem.override {
        exec = "env MOZ_USE_XINPUT2=1 firefox --name firefox %U";
      };
    });
    profiles.xieby1 = {
      # id is default 0, thus this profile is default
      extensions = 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
      ];
      settings = {
        # Automatically enable extensions
        "extensions.autoDisableScopes" = 0;
      };
    };
  };
}

Gnome in NixOS

插件

桌面快捷键

{ config, pkgs, stdenv, lib, ... }:
# gnome extensions and settings
# no need log out to reload extension: <alt>+F2 r
let
  opt = import ../../opt.nix;
in{
  home.packages = (with pkgs; [
    gnome.gnome-sound-recorder
    gnome.dconf-editor
    gnome.devhelp
  ])
  ++ (with pkgs.gnomeExtensions; [
    unite
    clipboard-indicator
    bing-wallpaper-changer
    # random-wallpaper-wip-v3
    gtile
    pkgs.pkgsu.gnomeExtensions.hide-top-bar
    dash-to-dock
    always-show-titles-in-overview
    customize-ibus
    # replace system-monitor(-next) with vitals
    # refers to https://github.com/mgalgs/gnome-shell-system-monitor-applet/issues/57
    # vitals
    system-monitor-next
  ]);

  # Setting: `gsettings set <key(dot)> <value>`
  # Getting: `dconf dump /<key(path)>`
  dconf.settings = {
    "org/gnome/shell" = {
      disable-extension-version-validation = true;
      ## enabled gnome extensions
      disable-user-extensions = false;
      disabled-extensions = [];
      enabled-extensions = [
        "BingWallpaper@ineffable-gmail.com"
        "clipboard-indicator@tudmotu.com"
        "gTile@vibou"
        "hidetopbar@mathieu.bidon.ca"
        # "Vitals@CoreCoding.com"
        "system-monitor-next@paradoxxx.zero.gmail.com"
        "unite@hardpixel.eu"
        "dash-to-dock@micxgx.gmail.com"
        # "randomwallpaper@iflow.space"
        "Always-Show-Titles-In-Overview@gmail.com"
        "customize-ibus@hollowman.ml"
      ];

      ## dock icons
      favorite-apps = [
        "org.gnome.Nautilus.desktop"
        "firefox.desktop"
        "microsoft-edge.desktop"
        "calendar.desktop"
        "todo.desktop"
        "spotify.desktop"
        "google-chrome.desktop"
        ];
    };
    ## extensions settings
    # "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" = {
      compact-display = true;
      icon-display = false;
      cpu-style = "digit";
      memory-style = "digit";
      net-style = "digit";
    };
    "org/gnome/shell/extensions/gtile" = {
      animation=true;
      global-presets=true;
      grid-sizes="6x4,8x6";
      preset-resize-1=["<Super>bracketleft"];
      preset-resize-2=["<Super>bracketright"];
      preset-resize-3=["<Super>period"];
      preset-resize-4=["<Super>slash"];
      preset-resize-5=["<Super>apostrophe"];
      preset-resize-6=["<Super>semicolon"];
      preset-resize-7=["<Super>comma"];
      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;
    };

    "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";
    };
    "org/gnome/shell/extensions/hidetopbar" = {
      mouse-sensitive = true;
      enable-active-window=false;
      enable-intellihide=true;
      shortcut-delay = 0.0;
      shortcut-keybind = ["<Super>h"];
    };
    "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;
    };
    "org/gnome/shell/extensions/bingwallpaper" = {
      market="zh-CN";
      delete-previous=true;
      download-folder="/tmp/pictures";
    };
    "org/gnome/shell/extensions/dash-to-dock" = {
      click-action="focus-or-appspread";
    };
    "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";
    };


    # 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>Home"];
      move-to-workspace-right=["<Control>End"];
    };
    "org/gnome/shell/extensions/clipboard-indicator" =
    {
      move-item-first=true;
    };

    # nautilus
    "org/gtk/settings/file-chooser" = {
      sort-directories-first=true;
    };

    "org/gnome/desktop/interface" = {
      enable-hot-corners=false;
      show-battery-percentage=true;
      switch-input-source=["<Control>space"];
      switch-input-source-backward=["<Shift><Control>space"];
    };

    # proxy
    "system/proxy" = {mode = "manual";};
    "system/proxy/ftp" = {host="127.0.0.1"; port=opt.proxyPort;};
    "system/proxy/http" = {host="127.0.0.1"; port=opt.proxyPort;};
    "system/proxy/https" = {host="127.0.0.1"; port=opt.proxyPort;};

    # input method
    "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;
    };
    "org/gnome/mutter" = {
      dynamic-workspaces = true;
    };
  };

  # inspired by https://discourse.nixos.org/t/how-to-set-the-bookmarks-in-nautilus/36143
  home.activation.nautilus_bookmarks = lib.hm.dag.entryAfter [ "writeBoundary" ] ''
    # WSL2 may not have folder ~/.config/gtk-3.0
    $DRY_RUN_CMD mkdir -p $VERBOSE_ARG ~/.config/gtk-3.0
    $DRY_RUN_CMD ln -sf $VERBOSE_ARG ~/Gist/Config/nautilus_bookmarks ~/.config/gtk-3.0/bookmarks
  '';
}

KDE Connect

{ config, pkgs, stdenv, lib, ... }:
let
  mykdeconnect = pkgs.kdeconnect;

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";
  };
}
# 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_x/"
  ];
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_window" = {
    binding="<Super>w";
    command="rofi -show window";
    name="rofi window";
  };
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/rofi_x" = let
    x = pkgs.writeScript "x" ''
      cd ${config.home.homeDirectory}/Gist/script/bash/
      xdotool type --clearmodifiers --delay 30 \
        "$(ls --color=never x-* | rofi -dmenu | xargs bash -c)"
    '';
  in {
    binding="<Super>x";
    command="${x}";
    name="rofi x";
  };

  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 offical 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, lib, ... }:
let
  # You Can Change To Chrome-Like Browser Here!
  chromeLikeBrowser = if (lib.meta.availableOn builtins.currentSystem pkgs.microsoft-edge.meta.platforms)
    then "${pkgs.microsoft-edge}/bin/microsoft-edge"
    else "${pkgs.chromium}/bin/chromium";

  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";

  webapp_common = ''
    if [[ $# -lt 2 || "$1" == "-h" ]]
    then
      echo "Usage: ''${0##*/} <window> <url>"
      echo "  Only start a webapp once, if the app is running"
      echo "  then bring it to foreground"
      exit 0
    fi

    # check URL prefix
    URL=$2
    if [[ "$URL" =~ ^~ ]]
    then
      URL="file://$HOME/''${URL#\~/}"
    fi
    if [[ "$URL" =~ ^\/ ]]
    then
      URL="file://$URL"
    fi
    if [[ "$URL" =~ ^(file|https?)?:\/\/ ]]
    then
      true
    else
      URL="https://$URL"
    fi
  '';
  webapp = pkgs.writeShellScriptBin "webapp.sh" ''
    ${webapp_common}
    ${singleton_sh} "$1" ${chromeLikeBrowser} --app="$URL"
  '';
  webapp_no_cors = pkgs.writeShellScriptBin "webapp_no_cors.sh" ''
    ${webapp_common}
    ${singleton_sh} "$1" ${chromeLikeBrowser} --user-data-dir=~/.chrome_no_cors --disable-web-security --app="$URL"
  '';
  webapp_sh = "${webapp}/bin/webapp.sh";
  webapp_no_cors_sh = "${webapp_no_cors}/bin/webapp_no_cors.sh";
  open_my_cheatsheet_md_sh = pkgs.writeShellScript "open_my_cheatsheet_md" ''
     cd ${config.home.homeDirectory}/Documents/Tech
     kitty nvim my_cheatsheet.mkd -c Vista
     make
  '';
in
{
  home.packages = [singleton webapp webapp_no_cors];

  # gnome keyboard shortcuts
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys".custom-keybindings = [
    "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/my_cheatsheet_html/"
    "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/my_cheatsheet_md/"
    "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/bing_dict/"
    "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/hjxd_jp/"
    "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/kdeconnect_app/"
    "/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/devdocs/"
  ];
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/my_cheatsheet_html" = {
    binding="<Alt>space";
    command="gtk-launch my_cheatsheet_html.desktop";
    name="cheatsheet";
  };
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/my_cheatsheet_md" = {
    binding="<Alt>c";
    command="gtk-launch my_cheatsheet_md.desktop";
    name="edit cheatsheet";
  };
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/bing_dict" = {
    binding="<Alt>b";
    command="gtk-launch bing_dict.desktop";
    name="bing dict";
  };
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/hjxd_jp" = {
    binding="<Alt>j";
    command="gtk-launch hjxd_jp.desktop";
    name="日语词典";
  };
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/kdeconnect_app" = {
    binding="<Alt>k";
    command="gtk-launch kdeconnect_app.desktop";
    name="KDE Connect";
  };
  dconf.settings."org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/devdocs" = {
    binding="<Alt>d";
    command="gtk-launch devdocs.desktop";
    name="Devdocs";
  };

  xdg.desktopEntries = {
    # web apps
    ## microsoft's
    todo = {
      name = "Microsoft To Do";
      genericName = "ToDo";
      exec = "${webapp_sh} \"To Do\" https://to-do.live.com/";
      icon = builtins.fetchurl {
        url = "http://www.google.com/s2/favicons?domain=https://to-do.live.com&sz=128";
        name = "ToDo.png";
      };
    };
    calendar = {
      name = "Microsoft Calendar";
      exec = "${webapp_sh} Outlook https://outlook.live.com/calendar";
      icon = (pkgs.fetchurl {
        url = "https://cdn.cdnlogo.com/logos/o/82/outlook.svg";
        sha256 = "0544z9vmghp4lgapl00n99vksm0gq8dfwrp7rvfpp44njnh6b6dz";
      }).outPath;
    };
    outlook = {
      name = "Microsoft Outlook";
      exec = "${webapp_sh} Outlook https://outlook.live.com";
      icon = (pkgs.fetchurl {
        url = "https://cdn.cdnlogo.com/logos/o/82/outlook.svg";
        sha256 = "0544z9vmghp4lgapl00n99vksm0gq8dfwrp7rvfpp44njnh6b6dz";
      }).outPath;
    };
    word = {
      name = "Word";
      genericName = "office";
      exec = "${webapp_sh} Word https://www.office.com/launch/word";
      icon = (pkgs.fetchurl {
        url = "https://cdn.cdnlogo.com/logos/w/19/word.svg";
        sha256 = "1ig0d8afacfl7m1n0brx82iw8c2iif3skb8dwjly4fzxikzvfmn4";
      }).outPath;
    };
    excel = {
      name = "Excel";
      genericName = "office";
      exec = "${webapp_sh} Excel https://www.office.com/launch/excel";
      icon = (pkgs.fetchurl {
        url = "https://cdn.cdnlogo.com/logos/m/96/microsoft-excel.png";
        sha256 = "07ch9kb3s82m47mm414gvig6zg2h4yffmvjvg7bvr7sil8476cs8";
      }).outPath;
    };
    powerpoint = {
      name = "PowerPoint";
      genericName = "office ppt";
      exec = "${webapp_sh} PowerPoint https://www.office.com/launch/powerpoint";
      icon = (pkgs.fetchurl {
        url = "https://cdn.cdnlogo.com/logos/p/67/powerpoint.svg";
        sha256 = "1pnb2nna2b26kyn0i92xmgdpcrqhw1cpl3vv7vvvlsxrldndhclr";
      }).outPath;
    };
    onedrive = {
      name = "OneDrive";
      exec = "${webapp_sh} OneDrive https://onedrive.live.com";
      icon = (pkgs.fetchurl {
        url = "https://cdn.cdnlogo.com/logos/m/73/microsoft-onedrive.svg";
        sha256 = "10sjz81xjcqfkd7v11vhpvdp0s2a8la9wipc3aapgybg822vhjck";
      }).outPath;
    };
    ## others
    suishouji = {
      name = "随手记";
      genericName = "suishouji";
      exec = "${webapp_sh} 随手记 https://www.sui.com/";
      icon = (pkgs.fetchurl {
        url = "https://res.sui.com/favicon.ico";
        sha256 = "01vm275n169r0ly8ywgq0shgk8lrzg79d1aarshwybwxwffj4q0q";
      }).outPath;
    };
    webweixin = {
      name = "网页微信";
      genericName = "weixin";
      exec = ''${webapp_sh} "微信|weixin" https://wx.qq.com/'';
      icon = (pkgs.fetchurl {
        url = "https://cdn.cdnlogo.com/logos/w/79/wechat.svg";
        sha256 = "1xk1dsia6favc3p1rnmcncasjqb1ji4vkmlajgbks0i3xf60lskw";
      }).outPath;
    };
    my_cheatsheet_html = {
      name = "Cheatsheet HTML";
      genericName = "cheatsheet";
      exec = ''${webapp_sh} "markdown cheatsheet" ${config.home.homeDirectory}/Documents/Tech/my_cheatsheet.html'';
    };
    bing_dict = {
      name = "Bing Dict";
      genericName = "dictionary";
      exec = ''${webapp_sh} "搜索 词典" https://cn.bing.com/dict/'';
    };
    hjxd_jp = {
      name = "日语词典";
      genericName = "riyucidian";
      exec = "${webapp_sh} 日语词典 https://dict.hjenglish.com/jp/";
    };
    devdocs = {
      name = "DevDocs";
      genericName = "devdocs";
      exec = "${webapp_sh} DevDocs https://devdocs.io/";
      icon = (pkgs.fetchurl {
        url = "https://devdocs.io/images/webapp-icon-512.png";
        sha256 = "0bbimjp8r4fwzgd094wady2ady1fqz0crnyy2iwa835g7yivix24";
      }).outPath;
    };
    clash = let
      metacubexd = builtins.fetchTarball "https://github.com/MetaCubeX/metacubexd/archive/gh-pages.zip";
    in {
      name = "clash";
      exec = "${webapp_no_cors_sh} metacubexd file://${metacubexd}/index.html";
      icon = "${metacubexd}/pwa-192x192.png";
    };

    # singleton apps
    my_cheatsheet_md = {
      name = "Cheatsheet MD";
      genericName = "cheatsheet";
      exec = "${singleton_sh} my_cheatsheet.mkd ${open_my_cheatsheet_md_sh}";
    };
    kdeconnect_app = {
      name = "(S) KDE Connect App";
      genericName = "kdeconnect";
      exec = "${singleton_sh} kdeconnect.app kdeconnect-app";
    };
  };
}
{ 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!

  # 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 = {
      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
      map ctrl+shift+f launch --location=hsplit --allow-remote-control kitty +kitten search.py @active-kitty-window-id

      # disable opening of URLs with a plain click
      mouse_map left click ungrabbed no_op

      #: moves the window into a new tab
      map f1 detach_window new-tab
      #: 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
    '';
  };

  home.file.kitty_search = {
    source = pkgs.fetchurl {
      url = "https://github.com/trygveaa/kitty-kitten-search/raw/0760138fad617c5e4159403cbfce8421ccdfe571/search.py";
      sha256 = "1w50fimqsbmqk9zhdmq8k2v1b36iwsglpbqaavpglw0acam3xid7";
    };
    target = ".config/kitty/search.py";
  };
  home.file.kitty_scrool_mark = {
    source = pkgs.fetchurl {
      url = "https://github.com/trygveaa/kitty-kitten-search/raw/0760138fad617c5e4159403cbfce8421ccdfe571/scroll_mark.py";
      sha256 = "1a1l7sp2x247da8fr54wwq7ffm987wjal9nw2f38q956v3cfknzi";
    };
    target = ".config/kitty/scroll_mark.py";
  };
}

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进行二次打包。

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];
}

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";
  })];};
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的配置./home.nix。 下面是我的带注解的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的配置文件./home.nix的配置。 如此操作,所有home-manager的软件和配置都能nix-on-droid中复用。

  home-manager.config = import ./home.nix;
}

用于自动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 = ''
        echo Pushing packages to cachix:
        ${lib.concatMapStrings (x: "echo 📦"+x+"\n") config.cachix_packages}
        ${pkgs.cachix}/bin/cachix -c ${config.cachix_dhall} push ${config.cachix_name} ${builtins.toString config.cachix_packages}
      '';
      description = ''
        (Internal usage) The script of pushing packages to cachix.
      '';
    };
  };
}

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

注意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
        • allPackages import pkgs/top-level/stage.nix
          • allPackages import pkgs/top-level/all-packages.nix
        • boot = import pkgs/stdenv/booter.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:

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:

ScenarioHookExample
home-managerhome.activationmodules/cachix.nix, usr/modules/cachix.nix
nixos-rebuildsystem.activationScriptsmodules/cachix.nix, sys/modules/cachix.nix
nix-shellshellHookopenc910/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 备份的/nix/store如何使用?

交叉编译和安装跨平台程序

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的例子

    shell_cross_platform.nix

目录

简介

nixpkgs1众多输入参数中,包含localSystemcrossSystem2

  • localSystem

    The system packages will be built on.

    本地系统,即工具链运行的平台。

  • crossSystem

    The system packages will ultimately be run on.

    程序运行的平台。

通过localSystemcrossSystem不同值的组合, 可以实现交叉编译、安装其他架构的原生应用。 下面从localSystemcrossSystem的语法和应用两方面进行介绍。 语法章节从nixpkgs源码的角度出发,介绍其语法的组成。 应用章节围绕一个nix-shell脚本的实际例子, 介绍x86_64平台的交叉编译和安装aarch64架构的原生应用的方法。

localSystemcrossSystem的语法

localSystemcrossSystem由4个维度去刻画一个系统:cpu, vendor, kernel, abi。 localSystemcrossSystem的值为字符串或者{system=字符串;}3。 system字符串为可以包含前述4个维度的1~4个维度。 nix在解析时会将省略的维度按以某些默认值补充完整。 维度之间之间用-分割。 因此system字符串形式上为"cpu-vendor-kernel-abi"。 字符串不同数量的维度及其可用的值, 按匹配优先级由高到低列举如下4

cpu-vendor-kernel-abi

system字符串cpuvendorkernelabi
"avr"avrnoneunknown
"{cpu}-cygwin"{cpu}windowscygnus
"{cpu}-windows"{cpu}windowsmsvc
"{cpu}-elf"{cpu}unknownnoneelf
"{cpu}-{kernel}"{cpu}{kernel}
"{cpu}-apple-{kernel}"{cpu}apple{kernel}
"{cpu}-linux-gnu"{cpu}linuxgnu
"{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}unknownghcjs
"{cpu}-{vendor}-genode"{cpu}{vendor}genode
"{cpu}-{vendor}-{kernel}-{abi} "{cpu}{vendor}{kernel}{abi}

cpu

cpu字符串可取的值列举如下5,

cpu字符串bitssignificantBytefamilyversionarch
"arm"32littleEndian"arm"
"armv5tel"32littleEndian"arm""5""armv5t"
"armv6m"32littleEndian"arm""6""armv6-m"
"armv6l"32littleEndian"arm""6""armv6"
"armv7a"32littleEndian"arm""7""armv7-a"
"armv7r"32littleEndian"arm""7""armv7-r"
"armv7m"32littleEndian"arm""7""armv7-m"
"armv7l"32littleEndian"arm""7""armv7"
"armv8a"32littleEndian"arm""8""armv8-a"
"armv8r"32littleEndian"arm""8""armv8-a"
"armv8m"32littleEndian"arm""8""armv8-m"
"aarch64"64littleEndian"arm""8""armv8-a"
"aarch64_be"64bigEndian"arm""8""armv8-a"
"i386"32littleEndian"x86""i386"
"i486"32littleEndian"x86""i486"
"i586"32littleEndian"x86""i586"
"i686"32littleEndian"x86""i686"
"x86_64"64littleEndian"x86""x86-64"
"mips"32bigEndian"mips"
"mipsel"32littleEndian"mips"
"mips64"64bigEndian"mips"
"mips64el"64littleEndian"mips"
"mmix"64bigEndian"mmix"
"m68k"32bigEndian"m68k"
"powerpc"32bigEndian"power"
"powerpc64"64bigEndian"power"
"powerpc64le"64littleEndian"power"
"powerpcle"32littleEndian"power"
"riscv32"32littleEndian"riscv"
"riscv64"64littleEndian"riscv"
"s390"32bigEndian"s390"
"s390x"64bigEndian"s390"
"sparc"32bigEndian"sparc"
"sparc64"64bigEndian"sparc"
"wasm32"32littleEndian"wasm"
"wasm64"64littleEndian"wasm"
"alpha"64littleEndian"alpha"
"msp430"16littleEndian"msp430"
"avr"8"avr"
"vc4"32littleEndian"vc4"
"or1k"32bigEndian"or1k"
"js"32littleEndian"js"
cpuTypes

cpu之间的兼容性(具有传递性和自反性)如下6

vendor

vendor字符串可取值"apple", "pc"(windows), "w64"(MinGW-w64), "none", "unknown"(default)。

kernel

kernel字符串可取值如下表7

kernel字符串execFormatfamilies
"macos"machodarwin
"darwin"
"ios"machodarwin
"watchos"
"tvos"
"freebsd"elfbsd
"linux"elf
"netbsd"elfbsd
"none"unknown
"openbsd"elfbsd
"solaris"elf
"wasi"wasm
"redox"elf
"windows"pe
"win32"
"ghcjs"unknown
"genode"elf
"mmixware"unknown

abi

abi字符串可取的值列举如下8

abi字符串floatabiNote
"cygnus"
"msvc"
"eabi"softfor ARM, PowerPC
"eabihf"hardfor 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"

localSystemcrossSystem的应用

aarch64交叉工具链和程序的详细例子

以x86为本地指令集,localSystemcrossSystem的组合有以下效果

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)

引用

1

nixpkgs版本2022.01.20, commit hash: 7e149abe9db1509fa974bb2286f862a761ca0a07

2

nixpkgs/pkgs/top-level/default.nix

3

nixpkgs/lib/systems/default.nix: elaborate

4

nixpkgs/lib/systems/parse.nix: mkSkeletonFromList

5

nixpkgs/lib/systems/parse.nix: cpuTypes

6

nixpkgs/lib/systems/parse.nix: isCompatible

7

nixpkgs/lib/systems/parse.nix: kernels

8

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

物理机双系统:

还未探索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。

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
  '';
})
2022.04.19

在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)。

模拟器闪退无法启动,暂时没有尝试去解决。

参考

使用的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...

相关问题

从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

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
  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的构建主要由markcodemdbook这两个工具支持。

  • 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
  ];
}