From 06b20b61be9897f1c26add3327e81359c2f45dc2 Mon Sep 17 00:00:00 2001 From: Weathercold Date: Mon, 28 Jul 2025 00:48:58 -0400 Subject: [PATCH] feat(nix): add nix flake and package (#319) * feat(nix): add nix flake and package * feat: support darwin --------- Co-authored-by: Neko --- .gitignore | 5 ++ default.nix | 1 + flake.lock | 27 ++++++++ flake.nix | 50 ++++++++++++++ package.nix | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 270 insertions(+) create mode 100644 default.nix create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 package.nix diff --git a/.gitignore b/.gitignore index 0227bdeb1..4a7fbb341 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,8 @@ web-ext.config.ts # Data .postgres twitter-session.json + +# Symlinks created by nix build +result* + + diff --git a/default.nix b/default.nix new file mode 100644 index 000000000..0990d9c4b --- /dev/null +++ b/default.nix @@ -0,0 +1 @@ +(builtins.getFlake (toString ./.)).packages.${builtins.currentSystem} diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..8530de990 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1752950548, + "narHash": "sha256-NS6BLD0lxOrnCiEOcvQCDVPXafX1/ek1dfJHX1nUIzc=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "c87b95e25065c028d31a94f06a62927d18763fdf", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..5ff75c2e4 --- /dev/null +++ b/flake.nix @@ -0,0 +1,50 @@ +{ + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = + { self, nixpkgs }: + let + systems = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + pkgsForSystem = + system: + import nixpkgs { + inherit system; + config.allowUnfree = true; + }; + in + { + formatter = forAllSystems (system: (pkgsForSystem system).nixfmt-tree); + + packages = forAllSystems (system: { + default = self.packages.${system}.airi; + airi = (pkgsForSystem system).callPackage ./package.nix { }; + airi-debug = (pkgsForSystem system).callPackage ./package.nix { debugBuild = true; }; + }); + + overlays = { + default = self.overlays.airi; + airi = final: _: { + airi = final.callPackage ./package.nix { }; + }; + }; + + devShells = forAllSystems ( + system: with (pkgsForSystem system); { + default = mkShell { + inputsFrom = [ self.packages.${system}.airi ]; + packages = [ + nixd + nixfmt-tree + pnpm + ]; + }; + } + ); + }; +} diff --git a/package.nix b/package.nix new file mode 100644 index 000000000..4c49736ad --- /dev/null +++ b/package.nix @@ -0,0 +1,187 @@ +{ + config, + lib, + stdenv, + stdenvNoCC, + rustPlatform, + + autoPatchelfHook, + cargo-tauri, + cudaPackages, + pkg-config, + pnpm, + wrapGAppsHook3, + + alsa-lib, + atk, + cacert, + glib, + libayatana-appindicator, + nodejs, + onnxruntime, + openssl, + webkitgtk_4_1, + + cudaSupport ? config.cudaSupport, + debugBuild ? false, +}: + +rustPlatform.buildRustPackage (final: { + pname = "airi"; + version = "0.7.0-alpha.1"; + + src = ./.; + + cargoLock.lockFile = ./Cargo.lock; + + pnpmDeps = pnpm.fetchDeps { + inherit (final) pname version src; + fetcherVersion = 1; + hash = "sha256-gqsJ2TDby15bhz7AgiaRs06p0XiJY4w9Ay1HtRV28nk="; # To update, set it to "" + }; + + # Cache of assets downloaded during vite build + assets = stdenvNoCC.mkDerivation { + pname = "${final.pname}-assets"; + inherit (final) version src pnpmDeps; + + nativeBuildInputs = [ + cacert # For network request + nodejs + pnpm.configHook + ]; + + buildPhase = '' + runHook preBuild + + pnpm run build:packages + pnpm -F @proj-airi/stage-tamagotchi run build + + runHook postBuild + ''; + + installPhase = '' + runHook preInstall + + mkdir -p $out + cp -r apps/stage-tamagotchi/.cache/assets $out + + runHook postInstall + ''; + + outputHashMode = "recursive"; + # To update, set it to lib.fakeHash + outputHash = "sha256-QDGx5sWlWwgMWvG/umkNY+Ct9i5zl+eKEJnvA2whPkY="; + }; + + nativeBuildInputs = + [ + cargo-tauri.hook + nodejs + pkg-config + pnpm.configHook + ] + ++ lib.optionals stdenv.isLinux [ + wrapGAppsHook3 + autoPatchelfHook + ] + ++ lib.optionals cudaSupport [ + cudaPackages.cuda_nvcc # Used by cudarc and bindgen_cuda + ]; + + buildInputs = [ + glib # Used by glib-sys + onnxruntime # Used by ort-sys + openssl # Used by openssl-sys + ] + ++ lib.optionals stdenv.isLinux [ + alsa-lib # Used by alsa-sys + atk # Used by atk-sys + libayatana-appindicator # Used by libappindicator-sys + webkitgtk_4_1 + ] + ++ lib.optionals cudaSupport [ + cudaPackages.cuda_cccl # For nv/target used by bindgen_cuda + cudaPackages.cuda_cudart # For cuda.h used by cudarc + cudaPackages.cuda_nvrtc + cudaPackages.libcublas + cudaPackages.libcurand + ]; + + env = lib.optionalAttrs cudaSupport { + # For bindgen_cuda + CUDA_COMPUTE_CAP = builtins.replaceStrings [ "." ] [ "" ] ( + builtins.head cudaPackages.flags.cudaCapabilities + ); + }; + + configurePhase = '' + runHook preConfigure + + echo Setting up asset cache + mkdir apps/stage-tamagotchi/.cache + cp -r $assets/assets apps/stage-tamagotchi/.cache + + ${lib.optionalString cudaSupport '' + echo Patching cudarc build script to find CUDA + # In configurePhase because CUDA is set up in a pre configure hook + # cudarc searches in a few environment variables, but they can't be used because they don't + # support the CUDAToolkit_ROOT format (semicolon-separated list of paths). + # https://github.com/coreylowman/cudarc/blob/main/build.rs#L211 + substituteInPlace ../cargo-vendor-dir/cudarc-*/build.rs \ + --replace-fail /usr/lib/cuda "$(echo "$CUDAToolkit_ROOT" | sed 's/;/", "/g')" + + echo Patching bindgen_cuda to find CUDA # Used by candle-kernels + # bindgen_cuda copies the pathfinding logic from cudarc + # https://github.com/Narsil/bindgen_cuda/blob/main/src/lib.rs#L436 + substituteInPlace ../cargo-vendor-dir/bindgen_cuda-*/src/lib.rs \ + --replace-fail /usr/lib/cuda "$(echo "$CUDAToolkit_ROOT" | sed 's/;/", "/g')" + ''} + + runHook postConfigure + ''; + + preBuild = '' + pnpm run build:packages + ''; + + buildAndTestSubdir = "apps/stage-tamagotchi"; + buildType = if debugBuild then "debug" else "release"; + cargoBuildFeatures = lib.optional cudaSupport "cuda"; + + postInstall = + lib.optionalString stdenv.isLinux '' + mv $out/bin/app $out/bin/airi + '' + + lib.optionalString stdenv.isDarwin '' + mkdir -p "$out/bin" + ln -sf "$out/Applications/AIRI.app/Contents/MacOS/app" "$out/bin/airi" + ''; + + # Add missing runtime dependency + preFixup = lib.optionalString stdenv.isLinux '' + patchelf --add-needed libayatana-appindicator3.so.1 $out/bin/airi + ''; + + meta = { + description = "Self-hostable AI waifu / companion / VTuber"; + longDescription = '' + AIRI is a container of souls of AI waifu / virtual characters to bring them into our worlds, + wishing to achieve Neuro-sama's altitude, completely LLM and AI driven, capable of realtime + voice chat, Minecraft playing, Factorio playing. It can be run in Browser or Desktop. + ''; + homepage = "https://github.com/moeru-ai/airi"; + changelog = "https://github.com/moeru-ai/airi/releases/tag/v${final.version}"; + # While airi itself is licensed under MIT, it uses the nonfree Cubism SDK. Whether it's + # redistributable remains a question, so we say it's not. + license = lib.licenses.unfree; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + ]; + mainProgram = "airi"; + maintainers = with lib.maintainers; [ weathercold ]; + }; +})