diff --git a/.gitignore b/.gitignore index d9a18cc..fe3e4d0 100755 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ build __pycache__ configure.mk result +gcroots diff --git a/Makefile b/Makefile index 9649427..0b5131a 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,9 @@ INCDIR = build/inc PREFIX ?= /usr +PYTHON_VERSION = $(shell python -c 'import sys; print(f"{sys.version_info.major}.{sys.version_info.minor}")') +PYTHON_PREFIX ?= $(PREFIX)/lib/python$(PYTHON_VERSION)/site-packages + # Directories that need to be created DIRS = $(BINDIR) $(OBJDIR) $(INCDIR) $(LIBDIR) $(SHAREDIR) @@ -47,7 +50,7 @@ MODULES = $(filter-out $(OBJDIR)/main%,$(OBJECTS)) # Build outputs GRAY = $(BINDIR)/gray LIBGRAY = $(LIBDIR)/libgray.so -BINARIES = $(GRAY) $(GRAY)-convert +BINARIES = $(GRAY) $(GRAY)-convert $(GRAY)-visual LIBRARIES = $(LIBGRAY) MANPAGES = $(addprefix $(SHAREDIR)/,gray.1 gray-convert.1 gray.ini.5 \ profiles.txt.5 beamdata.txt.5 magneticdata.txt.5) @@ -159,8 +162,13 @@ tests.%: $(GRAY) .PHONY: install-bin install-bin: $(BINARIES) $(LIBRARIES) mkdir -p $(PREFIX)/{bin,lib} - install -m555 -t $(PREFIX)/bin $(BINDIR)/* - install -m555 -t $(PREFIX)/lib $(LIBDIR)/* + install -m555 -t $(PREFIX)/bin $(BINARIES) + cp -fr -t $(PREFIX)/lib $(LIBRARIES) + +.PHONY: install-python +install-python: scripts/gray + mkdir -p $(PYTHON_PREFIX) + cp -fr -t $(PYTHON_PREFIX) $^ .PHONY: install-doc install-doc: $(SHAREDIR)/doc $(MANPAGES) @@ -171,7 +179,7 @@ install-doc: $(SHAREDIR)/doc $(MANPAGES) install -m644 -t $(PREFIX)/share/man/man5 $(SHAREDIR)/*.5 .PHONY: install -install: install-bin install-doc +install: install-bin install-python install-doc # dependencies $(OBJDIR)/%.o: $(OBJDIR)/%.d @@ -184,6 +192,12 @@ $(GRAY): $(OBJDIR)/main.o $(LIBGRAY) | $(BINDIR) $(GRAY)-convert: $(OBJDIR)/main_convert.o $(LIBGRAY) | $(BINDIR) $(LD) $(LDFLAGS) -o '$@' $< -lgray +# gray-visual script +$(GRAY)-visual: | $(BINDIR) + echo '#!/usr/bin/env python' > $@ + echo 'from gray.visual import main; main()' >> $@ + chmod +x $@ + # GRAY shared library $(LIBDIR)/libgray.so: $(MODULES) | $(LIBDIR) $(LD) -shared $(LDFLAGS) -o '$@' $^ diff --git a/default.nix b/default.nix index cca5ff0..dd79ccc 100644 --- a/default.nix +++ b/default.nix @@ -1,39 +1,61 @@ -{ static ? false }: +{ + static ? false, +}: let # Pinned Nixpkgs for reproducibility nixpkgs = builtins.fetchTarball - { name = "nixpkgs-23.05.5a237aecb572"; - url = "https://github.com/nixos/nixpkgs/archive/5a237aecb572.tar.gz"; + { name = "nixpkgs-23.05.5a237aecb572"; + url = "https://github.com/nixos/nixpkgs/archive/5a237aecb572.tar.gz"; sha256 = "166yxg4ff2jxvl9mbngd90dr1k3rdj6xmiccga41xynhh2zr0vmb"; }; # Needed for HTML manual - katex = builtins.fetchTarball { - url = "https://github.com/KaTeX/KaTeX/releases/download/v0.15.1/katex.tar.gz"; - sha256 = "007nv11r0z9fz593iwzn55nc0p0wj5lpgf0k2brhs1ynmikq9gjr"; - }; + katex = builtins.fetchTarball + { url = "https://github.com/KaTeX/KaTeX/releases/download/v0.15.1/katex.tar.gz"; + sha256 = "007nv11r0z9fz593iwzn55nc0p0wj5lpgf0k2brhs1ynmikq9gjr"; + }; - # Exclude this file, build artifacts and git - source = builtins.filterSource - (path: type: - !builtins.elem path [ "configure.mk" "default.nix" "result" ] - && baseNameOf path != "build" - && baseNameOf path != ".git") ./.; + # Exclude this file, .git and more from source + sieve = path: type: + !builtins.elem (baseNameOf path) ( + [ "default.nix" ".git" ] + ++ lib.splitString "\n" (lib.readFile ./.gitignore)); - inherit (import nixpkgs {}) lib pkgs; + inherit (import nixpkgs { }) lib pkgs; -in - pkgs.stdenv.mkDerivation rec { + python = pkgs.python3; + + gray-python = python.pkgs.buildPythonPackage { pname = "gray"; version = builtins.readFile ./.version; - src = source; + src = builtins.filterSource sieve ./.; + + format = "other"; + propagatedBuildInputs = with pkgs.python3Packages; [ numpy matplotlib ]; + makeFlags = [ "PREFIX=$(out)" ]; + + dontConfigure = true; + dontBuild = true; + installTargets = "install-python"; + + pythonImportsCheck = [ "gray" ]; + + meta = { + description = "Python interface to GRAY"; + platforms = python.meta.platforms; + }; + }; + +in + pkgs.stdenv.mkDerivation { + pname = "gray"; + version = builtins.readFile ./.version; + src = builtins.filterSource sieve ./.; doCheck = true; - checkInputs = with pkgs.python3Packages; [ - numpy scipy matplotlib - ]; + checkInputs = with python.pkgs; [ numpy scipy matplotlib ]; nativeBuildInputs = with pkgs; [ # fortran @@ -53,12 +75,13 @@ in nixpkgs ]; - buildInputs = lib.optional static pkgs.glibc.static; + buildInputs = + lib.singleton (python.withPackages (p: [ gray-python ])) + ++ lib.optional static pkgs.glibc.static; # fonts needed for the PDF manual - FONTCONFIG_FILE = pkgs.makeFontsConf { - fontDirectories = with pkgs; [ libertinus julia-mono ]; - }; + env.FONTCONFIG_FILE = pkgs.makeFontsConf + { fontDirectories = with pkgs; [ libertinus julia-mono ]; }; preConfigure = '' # set directories for temporary files @@ -75,6 +98,13 @@ in "--enable-deterministic" ]; + shellHook = '' + export PREFIX=build/ + export PYTHONPATH=build/${python.sitePackages} + ''; + + passthru.python = gray-python; + meta = { homepage = "https://doi.org/10.13182/FST07-A1494"; description = "A quasi-optical beam-tracing code for EC waves in tokamaks"; diff --git a/scripts/gray.py b/scripts/gray/__init__.py similarity index 100% rename from scripts/gray.py rename to scripts/gray/__init__.py diff --git a/scripts/gray_visual.py b/scripts/gray/visual.py similarity index 99% rename from scripts/gray_visual.py rename to scripts/gray/visual.py index 6fbd432..0e97bb0 100644 --- a/scripts/gray_visual.py +++ b/scripts/gray/visual.py @@ -1,6 +1,6 @@ ''' Script to quickly visualise both the inputs and output files of GRAY -Usage: python -m scripts.gray_visual -h +Usage: see gray-visual -h ''' from pathlib import Path @@ -10,7 +10,7 @@ from matplotlib.contour import ContourSet import argparse import matplotlib.pyplot as plt import numpy as np -import scripts.gray as gray +import gray def cli_args() -> argparse.Namespace: @@ -482,7 +482,7 @@ def plot_inputs(inputs: Path, outputs: Path, axes: [plt.Axes]): axes['D'].legend(loc='center left') -def main(kind: str, args: argparse.Namespace) -> (plt.Figure, [float]): +def draw(kind: str, args: argparse.Namespace) -> (plt.Figure, [float]): ''' Draws a single figure of a given kind ''' @@ -536,7 +536,7 @@ def main(kind: str, args: argparse.Namespace) -> (plt.Figure, [float]): return fig, rect -if __name__ == '__main__': +def main(): args = cli_args() def on_resize(event): @@ -550,7 +550,7 @@ if __name__ == '__main__': plt.ion() for kind in args.kind: - fig, rect = main(kind, args) + fig, rect = draw(kind, args) if args.interactive: fig.canvas.mpl_connect('resize_event', on_resize) if args.outfile is not None: @@ -563,3 +563,7 @@ if __name__ == '__main__': if args.interactive: input('press enter to quit') + + +if __name__ == '__main__': + main()