diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..3550a30f --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore index c29f0273..97efc395 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,5 @@ share/python-wheels/ .installed.cfg *.egg MANIFEST +# Nix build symlinks +result* diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..d71b3cf1 --- /dev/null +++ b/flake.lock @@ -0,0 +1,100 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1722284536, + "narHash": "sha256-kfivgxKUIGhia/Vtj+AQumdjc+4WZHdAty5pFOqxLLA=", + "owner": "doronbehar", + "repo": "nixpkgs", + "rev": "268f92b11d93ef2f3c132bd118ff6162b4e54e44", + "type": "github" + }, + "original": { + "owner": "doronbehar", + "ref": "pkg/misoc", + "repo": "nixpkgs", + "type": "github" + } + }, + "pyrp3": { + "flake": false, + "locked": { + "lastModified": 1712321147, + "narHash": "sha256-pL6hfDYRYyM2NcpX2k84IkeUFFIvazuE5X7/AU7bTCA=", + "owner": "linien-org", + "repo": "pyrp3", + "rev": "1bc29ae8a4618b0e7c8432f78011de24b667ece3", + "type": "github" + }, + "original": { + "owner": "linien-org", + "ref": "v2.0.1", + "repo": "pyrp3", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "gitignore": "gitignore", + "nixpkgs": "nixpkgs", + "pyrp3": "pyrp3" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..de4ed83d --- /dev/null +++ b/flake.nix @@ -0,0 +1,231 @@ +{ + description = "Spectroscopy lock application using RedPitaya"; + + # Updating this triggers a lot of rebuilds, since scipy has many dependents, + # prepare yourself before updating. + # Needs my fork for: https://github.com/NixOS/nixpkgs/pull/330928 + inputs.nixpkgs.url = "github:doronbehar/nixpkgs/pkg/misoc"; + inputs.flake-utils.url = "github:numtide/flake-utils"; + inputs.gitignore = { + url = "github:hercules-ci/gitignore.nix"; + # Use the same nixpkgs + inputs.nixpkgs.follows = "nixpkgs"; + }; + inputs.pyrp3 = { + url = "github:linien-org/pyrp3/v2.0.1"; + flake = false; + }; + + outputs = { self + , nixpkgs + , flake-utils + , gitignore + , ... + }@otherInputs: + flake-utils.lib.eachDefaultSystem (system: + let + # Credit @kranzes <3: https://github.com/NixOS/nix/issues/8163#issuecomment-1517774089 + flakeDate2human = flakeInput: builtins.concatStringsSep "-" (builtins.match "(.{4})(.{2})(.{2}).*" flakeInput.lastModifiedDate); + lockFile = builtins.fromJSON (builtins.readFile ./flake.lock); + pkgs = import nixpkgs { + inherit system; + }; + inherit (pkgs) lib; + pythonDevEnv = (python.withPackages(ps: builtins.attrValues { + inherit (ps) + click + fire + cma + matplotlib + migen + misoc + myhdl + numpy + paramiko + plumbum + pylpsd + pyqt5 + pyqtgraph + pyrp3 + pytest + pytest-plt + rpyc + superqt + scipy # From our fork + # For text editor + jedi-language-server + debugpy + # For testing installations + setuptools + setuptools-scm + ; + })).overrideAttrs (old: { + meta = old.meta // { + description = "Linien Python development environment"; + }; + }); + inherit (gitignore.lib) gitignoreFilterWith; + get-local-src = subdirectory: lib.cleanSourceWith { + filter = gitignoreFilterWith { + basePath = ./.; + extraRules = '' + flake* + ''; + }; + src = "${self}/${subdirectory}"; + }; + get-github-src-version = pname: { + src = pkgs.fetchFromGitHub { + inherit (lockFile.nodes.${pname}.original) + owner + repo + ; + sha256 = lockFile.nodes.${pname}.locked.narHash; + rev = lockFile.nodes.${pname}.locked.rev; + }; + version = if (builtins.hasAttr "ref" lockFile.nodes.${pname}.original) then + lockFile.nodes.${pname}.original.ref + else + flakeDate2human otherInputs.${pname} + ; + }; + linienBuildArgs = { + version = (builtins.fromTOML (builtins.readFile ./linien-server/pyproject.toml)).project.version; + }; + # Generate a `python` interpreter, with some python packages overriden + # and added - we merge the pythonOverrides of scipy-fork as well. We use + # lib.composeExtensions as instructed here: + # https://github.com/NixOS/nixpkgs/issues/44426 + pythonOverrides = lib.composeExtensions + # Empty override, may be useful in the future + (selfPython: superPython: {}) + (selfPython: superPython: { + linien-gui = superPython.python.pkgs.callPackage ./linien-gui/pkg.nix (linienBuildArgs // { + src = get-local-src "linien-gui"; + inherit (selfPython) + linien-client + pyqtgraph + ; + }); + linien-client = superPython.python.pkgs.callPackage ./linien-client/pkg.nix (linienBuildArgs // { + src = get-local-src "linien-client"; + inherit (selfPython) + linien-common + ; + }); + linien-common = superPython.python.pkgs.callPackage ./linien-common/pkg.nix (linienBuildArgs // { + src = get-local-src "linien-common"; + }); + linien-server = superPython.python.pkgs.callPackage ./linien-server/pkg.nix (linienBuildArgs // { + src = get-local-src "linien-server"; + inherit (selfPython) + linien-common + pylpsd + pyrp3 + ; + }); + pyrp3 = superPython.python.pkgs.callPackage + ./pyrp3 + (get-github-src-version "pyrp3") + ; + }) + ; + python = (pkgs.python3.override { + packageOverrides = pythonOverrides; + }).overrideAttrs(old: { + meta = old.meta // { + description = "Python interpreter with .pkgs set including linien"; + }; + }); + python-armv7l-hf-multiplatform = (pkgs.pkgsCross.armv7l-hf-multiplatform.python3.override { + packageOverrides = pythonOverrides; + }).overrideAttrs(old: { + meta = old.meta // { + description = "Python interpreter (cross compiled) with .pkgs set including linien"; + }; + }); + buildDeb = {pkg, targetArch, pkgName ? pkg.name}: pkgs.stdenv.mkDerivation { + name = "${pkg.name}.deb"; + buildInputs = [ + pkgs.dpkg + ]; + unpackPhase = "true"; + buildPhase = '' + export HOME=$PWD + mkdir -p pkgtree/nix/store/ + for item in "$(cat ${pkgs.referencesByPopularity pkg})"; do + cp -r $item pkgtree/nix/store/ + done + + mkdir -p pkgtree/bin + cp -r ${pkg}/bin/* pkgtree/bin/ + # We want Systemd files to be integrated with the target OS Systemd + if [[ -d ${pkg}/lib/systemd ]]; then + mkdir -p pkgtree/lib + cp -r ${pkg}/lib/systemd pkgtree/lib/ + fi + + chmod -R a+rwx pkgtree/nix + chmod -R a+rwx pkgtree/bin + mkdir pkgtree/DEBIAN + cat << EOF > pkgtree/DEBIAN/control + Package: ${pkgName} + Version: ${pkg.version} + Maintainer: "github.com/bleykauf" + '' + # TODO: Ideally we would parse `pkgs.stdenv.gcc.arch` or a similar + # attribute and use this argument such that dpkg-deb will be + # satisfied with our name of the platform. + + '' + Architecture: ${targetArch} + Description: ${pkg.meta.description} + EOF + ''; + installPhase = '' + dpkg-deb -b pkgtree + mv pkgtree.deb $out + ''; + meta = { + description = "Debian package of ${pkg.name} compiled for architecture ${targetArch}"; + }; + }; + in { + devShells = { + default = pkgs.mkShell { + nativeBuildInputs = [ + pythonDevEnv + # To inspect deb packages we build, using: + # + # dpkg --contents $(nix build --print-out-paths -L .\#linien-server-deb-armv7l-hf-multiplatform)` + pkgs.dpkg + # To manage linien.bin + pkgs.git-lfs + ]; + }; + }; + packages = { + # Put it here so it'll be easy to run commands such as: + # + # nix why-depends --all --derivation .\#python.pkgs.linien-gui .\#nixpkgs-python.pkgs.scipy + # + nixpkgs-python = pkgs.python3; + inherit pythonDevEnv; + # The server is built for debian only, so we don't inherit it here + inherit (python.pkgs) + linien-common + linien-client + linien-gui + ; + inherit + python + python-armv7l-hf-multiplatform + ; + linien-server-deb-armv7l-hf-multiplatform = buildDeb { + pkg = python-armv7l-hf-multiplatform.pkgs.linien-server; + targetArch = "armhf"; + pkgName = "linien-server"; + }; + }; + } + ); +} diff --git a/linien-client/pkg.nix b/linien-client/pkg.nix new file mode 100644 index 00000000..5c74b6e8 --- /dev/null +++ b/linien-client/pkg.nix @@ -0,0 +1,42 @@ +{ lib +, buildPythonPackage +, version +, src +, setuptools +, fabric +, typing-extensions +, linien-common +}: + +buildPythonPackage { + pname = "linien-client"; + inherit version; + pyproject = true; + + inherit src; + + nativeBuildInputs = [ + setuptools + ]; + + propagatedBuildInputs = [ + fabric + typing-extensions + linien-common + ]; + + # Same issue as explained in ../linien-common/pkg.nix + preBuild = '' + export HOME="$(mktemp -d)" + ''; + + pythonImportsCheck = [ + "linien_client" + ]; + + meta = with lib; { + description = "Client components of the Linien spectroscopy lock application"; + homepage = "https://github.com/linien-org/linien"; + license = licenses.gpl3; + }; +} diff --git a/linien-common/pkg.nix b/linien-common/pkg.nix new file mode 100644 index 00000000..45a6faf9 --- /dev/null +++ b/linien-common/pkg.nix @@ -0,0 +1,48 @@ +{ lib +, buildPythonPackage +, version +, src +, setuptools +, numpy +, scipy +, importlib-metadata +, rpyc +, appdirs +}: + +buildPythonPackage { + pname = "linien-common"; + inherit version; + pyproject = true; + + inherit src; + + nativeBuildInputs = [ + setuptools + ]; + + propagatedBuildInputs = [ + numpy + scipy + importlib-metadata + rpyc + appdirs + ]; + + # Even simply importing linien_common, requires having a valid home + # directory. Using preCheck is not early enough since the pythonImportsCheck + # phase is being run before. + preBuild = '' + export HOME="$(mktemp -d)" + ''; + + pythonImportsCheck = [ + "linien_common" + ]; + + meta = with lib; { + description = "Shared components of the Linien spectroscopy lock application"; + homepage = "https://github.com/linien-org/linien"; + license = licenses.gpl3; + }; +} diff --git a/linien-gui/pkg.nix b/linien-gui/pkg.nix new file mode 100644 index 00000000..9c1e8610 --- /dev/null +++ b/linien-gui/pkg.nix @@ -0,0 +1,80 @@ +{ lib +, buildPythonPackage +, version +, src +, setuptools +, requests +, pyqtgraph +, pyqt5 +, superqt +, click +, linien-client +, qt5 +, makeDesktopItem +, copyDesktopItems +, graphicsmagick +}: + +buildPythonPackage rec { + pname = "linien-gui"; + inherit version; + pyproject = true; + + inherit src; + + nativeBuildInputs = [ + setuptools + qt5.wrapQtAppsHook + copyDesktopItems + graphicsmagick + ]; + + buildInputs = [ + qt5.qtbase + qt5.qtwayland + ]; + + propagatedBuildInputs = [ + requests + pyqtgraph + pyqt5 + superqt + click + linien-client + ]; + + dontWrapQtApps = true; + preFixup = '' + makeWrapperArgs+=("''${qtWrapperArgs[@]}") + ''; + desktopItems = makeDesktopItem { + name = meta.mainProgram; + exec = meta.mainProgram; + icon = meta.mainProgram; + desktopName = meta.mainProgram; + comment = meta.description; + type = "Application"; + categories = [ "Science" ]; + }; + + postInstall = '' + mkdir -p $out/share/icons/hicolor/256x256/apps/ + gm convert linien_gui/icon.ico $out/share/icons/hicolor/256x256/apps/${meta.mainProgram}.png + ''; + + # Same issue as explained in ../linien-common/pkg.nix + preBuild = '' + export HOME="$(mktemp -d)" + ''; + + pythonImportsCheck = [ + "linien_gui" + ]; + + meta = with lib; { + description = "Graphical user interface of the Linien spectroscopy lock application"; + homepage = "https://github.com/linien-org/linien"; + license = licenses.gpl3; + mainProgram = "linien"; + }; +} diff --git a/linien-server/pkg.nix b/linien-server/pkg.nix new file mode 100644 index 00000000..34dc756b --- /dev/null +++ b/linien-server/pkg.nix @@ -0,0 +1,61 @@ +{ lib +, buildPythonPackage +, version +, src +, setuptools +, importlib-metadata +, cma +, fire +, pylpsd +, pyrp3 +, rpyc +, influxdb-client +, linien-common +}: + +buildPythonPackage { + pname = "linien-server"; + inherit version; + pyproject = true; + + inherit src; + + nativeBuildInputs = [ + setuptools + importlib-metadata + ]; + + propagatedBuildInputs = [ + cma + fire + influxdb-client + pylpsd + pyrp3 + rpyc + linien-common + ]; + + # The linien-server executable provides an `enable` subcommand that installs + # the service file to /etc/systemd/system (hardcoded). We do this so that the + # service could be enabled by debian builder at ../flake.nix . + postInstall = '' + install -Dm0644 \ + ./linien_server/linien-server.service \ + $out/lib/systemd/system/linien-server.service + ''; + + # Same issue as explained in ../linien-common/pkg.nix + preBuild = '' + export HOME="$(mktemp -d)" + ''; + + pythonImportsCheck = [ + "linien_server" + ]; + + meta = with lib; { + description = "Server components of the Linien spectroscopy lock application"; + homepage = "https://github.com/linien-org/linien"; + license = licenses.gpl3; + }; +} diff --git a/pyrp3/default.nix b/pyrp3/default.nix new file mode 100644 index 00000000..1beb4ac4 --- /dev/null +++ b/pyrp3/default.nix @@ -0,0 +1,45 @@ +{ lib +, src +, version +, buildPythonPackage +, setuptools +, setuptools-scm +, importlib-metadata +, myhdl +, rpyc +, cached-property +, numpy +}: + +buildPythonPackage { + pname = "pyrp3"; + inherit version; + pyproject = true; + + inherit src; + + nativeBuildInputs = [ + setuptools + setuptools-scm + ]; + + propagatedBuildInputs = [ + importlib-metadata + myhdl + rpyc + cached-property + numpy + ]; + + pythonImportsCheck = [ + "pyrp3" + "pyrp3.board" + ]; + + meta = with lib; { + description = "Python 3 port of PyRedPitaya library providing access to Red Pitaya registers"; + homepage = "https://github.com/linien-org/pyrp3"; + license = licenses.bsd3; + platforms = platforms.linux; + }; +}