diff --git a/olex2/README.md b/olex2/README.md index 7cc4032..cfd8f15 100644 --- a/olex2/README.md +++ b/olex2/README.md @@ -1,7 +1,164 @@ -# Olex2 +# olex2 -Olex2 is a free, open-source software program used for determining, visualizing, and analyzing small-molecule crystal structures. It provides a comprehensive and user-friendly graphical interface to guide users through the process of crystal structure solution, refinement, and report generation. This workflow-oriented program is designed to be useful for both experienced crystallographers and chemists who may not have specialized training. +Olex2 — open-source (BSD) small-molecule crystal structure solution, +refinement and analysis (olex2.refine / ShelX front-end, built on +cctbx/smtbx). This module provides **both** the GUI and the console build: -## Installation guide -1. First, download the zip archive with the binaries. For example for version 1.5, it was download from https://secure.olex2.org/olex2-distro/1.5/olex2-linux64.zip -2. Build the module with the correct version. +| launcher | what it is | +|---|---| +| `olex2` | wxWidgets GUI (needs an X display) | +| `olex2c` | console / "headless" Olex2 — same engine, no GUI, scriptable | + +- Upstream: https://www.olexsys.org/olex2 — install notes: + https://www.olexsys.org/olex2/docs/getting-started/installing-olex2/ +- Source: https://github.com/pcxod/olex2 (C++ engine; the HTML GUI lives in + `svn.olex2.org/olex2-gui`) — not used, see "Why the binary archive" +- License: BSD (https://www.olexsys.org/olex2/docs/getting-started/installing-olex2/open-source/) +- Download server: https://secure.olex2.org/olex2-distro/ (mirror + http://www2.olex2.org/olex2-distro/) + +## How it is built + +Upstream (install notes, "Linux"): *"We do not provide installers for Linux +– and provide zip files instead (Use the 'start' bash script to launch +Olex2)"* and *"Nothing needs to be compiled"*. The official +`olex2-distro/1.5/olex2-linux64.zip` (255 MB, ~1 GB unpacked) already +contains everything (verified by listing the archive, 2026-10-16): + +``` +olex2/olex2 GUI binary olex2/olex2c console binary (24 MB) +olex2/unirun vendor auto-updater olex2/start vendor launcher (runs unirun, then olex2) +olex2/cctbx/ cctbx_build + cctbx_sources olex2/lib/ private CPython 3.8 + libpython, openblas, ssl, png, ... +olex2/ilib/ olex2/util/pyUtil/ olex2/etc/ olex2/sample_data/ NoSpherA2, hart, hart_mpi ... +``` + +The recipe (`build`): + +1. default `pbuild::prep` downloads the zip per `urls:` in + `files/config.yaml` into the distfiles cache as + `olex2--linux64.zip`, verifies the pinned sha256 and copies it + to `SRC_DIR` (`unpacker: none`); +2. `configure` runs `unzip -t` (truncated-download guard) and unzips; +3. `install` copies the vendor tree **flat into `$PREFIX`** — Olex2 derives + its `BaseDir()` from the binary location, so the layout must stay as + shipped — fixes modes, switches `usettings.dat` to `update=Never`, and + installs `files/olex2-launcher` twice as `$PREFIX/bin/olex2` and + `$PREFIX/bin/olex2c`. Only `$PREFIX/bin` goes on `PATH`. + +`files/olex2-launcher` is the vendor `start` script minus the updater: it +exports `OLEX2_CCTBX_DIR`, `BOOST_ADAPTBX_*`, `OLEX2_GL_*`, +`LD_LIBRARY_PATH=$PREFIX/lib:$PREFIX/ilib:$PREFIX/cctbx/cctbx_build/lib`, +`PYTHONHOME=$PREFIX`, unsets `PYTHONPATH` (an inherited one would be +imported by the embedded Python 3.8 and break it), sets +`LIBGL_ALWAYS_SOFTWARE=1` for the GUI only, then `exec`s the binary. +`unirun` is never run: `/opt/psi/MX` is shared and read-only, updates are +done by building a new module version. + +### Why the binary archive and not a source build + +The user-facing "open source" route *is* this archive: Olex2 is BSD and the +official binaries are the open-source build. Compiling from +https://github.com/pcxod/olex2 is not a practical module recipe: the repo +builds only `olex2` (GUI, wxWidgets mandatory) and `unirun` — there is no +`olex2c` target in its CMakeLists/SConstruct/Makefile — the HTML GUI is in a +separate SVN, and the matching cctbx/smtbx build is not published. Its own +README says to take the official distribution and only swap in self-built +`olex2`/`unirun` binaries. The `Headless.zip`/`Batch.zip` next to the +archive on the download server are 2 KB plugin stubs, not builds. + +## Building on Ra / RHEL9 + +```sh +module use unstable && module load modbuild/2.1.3 +cd olex2 +modbuild build \ + --tmpdir=/das/work/p21/p21515/.cache/Pmodules \ + --distdir=/das/work/p21/p21515/.cache/Pmodules \ + 1.5-7665 +module use MX unstable && module load olex2/1.5-7665 +``` + +Expect a silent 10–20 min for the `cp -a` of ~12 000 files onto `/opt/psi`; +watch `du -sh /opt/psi/MX/olex2/1.5-7665` from a second shell. Needs +`unzip` on the build host. + +If the download server is unreachable from the build host (it is a plain +https URL, no login), fetch the zip anywhere and pre-stage it under the +exact name `/olex2-1.5-7665-linux64.zip`; prep then skips the +download but still checks the sha256. + +**RHEL9 runtime libraries.** The binaries bundle their own python, ssl, +png, jpeg, tiff, openblas, gfortran, but link system X11/GTK/OpenGL. On a +fresh RHEL9 host check once: + +```sh +module load olex2/1.5-7665 +ldd /opt/psi/MX/olex2/1.5-7665/olex2c | grep 'not found' # console: expect nothing +ldd /opt/psi/MX/olex2/1.5-7665/olex2 | grep 'not found' # GUI: typically gtk/mesa libs +``` + +and `dnf install` whatever the GUI line reports (likely candidates: +`gtk3`/`gtk2`, `mesa-libGL`, `mesa-libGLU`, `libSM`, `libXxf86vm`, +`libxkbcommon`, `libstdc++`). `olex2c` has far fewer needs and is the one to +verify first. Note `ldd` on the raw binary without the launcher's +`LD_LIBRARY_PATH` also lists the bundled libs as missing — only lines that +survive with the launcher env count: + +```sh +LD_LIBRARY_PATH=$P/lib:$P/ilib:$P/cctbx/cctbx_build/lib ldd $P/olex2c | grep 'not found' # P=/opt/psi/MX/olex2/1.5-7665 +``` + +## Verifying the install + +```sh +which olex2c # -> /opt/psi/MX/olex2/1.5-7665/bin/olex2c +which olex2 # -> /opt/psi/MX/olex2/1.5-7665/bin/olex2 +cat /opt/psi/MX/olex2/1.5-7665/version.txt # SVN Revision No. 7665 + +# console: starts, loads a shipped sample, refines, exits (writes to ~/.olex2 +# and to a scratch copy of the sample - run it in a scratch dir) +cd $(mktemp -d) && cp /opt/psi/MX/olex2/1.5-7665/sample_data/sucrose/* . +olex2c # interactive prompt; then type: +# reap sucrose.res +# refine 4 +# exit + +# GUI (needs X / x2go / NoMachine session): +olex2 +``` + +Scripting `olex2c`: it reads commands from stdin, so a batch job can do +`printf 'reap %s\nrefine 10\nexit\n' x.ins | olex2c` (to be confirmed on the +first RHEL9 test — the Windows `olex2c.exe` is used this way by CrysAlisPro +and the upstream NoSpherA2 release tests). The headless engine loads +`macrox.xld` instead of `macro.xld`; GUI-only python calls print +"not available in Headless Olex" rather than fail. + +First-start notes: Olex2 keeps user data in `~/.olex2` (DataDir), copies +sample data there and may try to delete `runonce.*` markers in BaseDir +(read-only for users — harmless). The vendor `start` and `unirun` remain +under `$PREFIX` but are intentionally not on PATH; do not run them against +the shared install. + +## Adding a new version + +`1.5/olex2-linux64.zip` is a **rolling file** — OlexSys replaces it in place +(check *Modified* on https://secure.olex2.org/olex2-distro/1.5/, or the +ALPHA/BETA series folders). The sha256 pinned in `files/config.yaml` will +therefore fail against a newer drop; that is the signal to add a version, +not to edit the hash: + +1. download the current zip and read its revision and hash: + `unzip -p olex2-linux64.zip olex2/version.txt` → e.g. `SVN Revision No. 7800`, + `sha256sum olex2-linux64.zip`; +2. add `1.5-7800` under `versions:` (`relstage: unstable`) and + `olex2-1.5-7800-linux64.zip: ` under `shasums:` — the `urls:` + entry needs no change (`${V_PKG}` = series dir, `${V}` = cache name); + for a 1.6 series just use `1.6-` and the URL follows; +3. build as above; promote to `stable` only after `olex2c` and `olex2` + have been exercised on the target host. + +History: `1.5` (stable) is the older Ra/RHEL8 module built by the previous +recipe from a hand-downloaded `olex2-linux64.zip` placed next to `build` +(never committed, `*.zip` is gitignored); it is kept in `config.yaml` so +the installed module stays listed. Do not rebuild it with this recipe. diff --git a/olex2/build b/olex2/build index d17357d..193b429 100755 --- a/olex2/build +++ b/olex2/build @@ -1,33 +1,72 @@ #!/usr/bin/env modbuild -pbuild::configure(){ - # Get the OS and architecture - os=$(uname -s | tr '[:upper:]' '[:lower:]') - arch=$(uname -m) - case "$arch" in - x86_64|amd64) arch="64" ;; - i386|i686) arch="32" ;; - aarch64) arch="arm64" ;; - armv7*) arch="arm" ;; - esac +# Olex2 - official open-source (BSD) Linux x86_64 distribution from OlexSys. +# https://www.olexsys.org/olex2/docs/getting-started/installing-olex2/ +# +# Upstream ships NO installer and NOTHING to compile for Linux: a single +# olex2-linux64.zip that already contains +# olex2 GUI (wxWidgets) olex2c console/"headless" Olex2 +# unirun vendor auto-updater (NOT used here) start vendor launcher (NOT used here) +# cctbx/ lib/ (private CPython 3.8 + libs) ilib/ util/ etc/ sample_data/ ... +# Default pbuild::prep downloads the zip (urls: in files/config.yaml), checks +# the pinned sha256 (shasums:) and copies it into SRC_DIR (unpacker: none). +# configure unzips, install copies the vendor tree 1:1 into $PREFIX (Olex2's +# BaseDir() is wherever the binaries sit) and adds $PREFIX/bin launchers. +# +# No errexit in modbuild hooks -> every critical step is die-guarded. - ZIP="${SRC_DIR}/${P}-${os}${arch}.zip" - - # Copy the zip archive from the build directory - cp -n "${BUILDBLOCK_DIR}/${P}-${os}${arch}.zip" "$ZIP" - - # Extract contents directly into SRC_DIR - unzip -o -q "$ZIP" -d "${SRC_DIR}" - - # Clean up the zip file - rm -f "$ZIP" +pbuild::configure() { + local -r zip="${SRC_DIR}/olex2-${V}-linux64.zip" + [[ -s "${zip}" ]] || std::die 42 "olex2: ${zip} not found - prep did not fetch it. \ +Pre-stage the archive by hand as /olex2-${V}-linux64.zip (see README) and re-run." + command -v unzip >/dev/null || std::die 42 "olex2: 'unzip' not on PATH on this build host" + # partial downloads reused from distfiles are the classic failure: test the archive first + unzip -tq "${zip}" >/dev/null || \ + std::die 42 "olex2: archive is corrupt/truncated - rm /olex2-${V}-linux64.zip and re-run" + # ~1 GB unpacked (cctbx + python + sample data); SRC_DIR is under --tmpdir + unzip -q -o "${zip}" -d "${SRC_DIR}" || std::die 42 "olex2: unzip failed (disk/quota at --tmpdir?)" + [[ -f "${SRC_DIR}/olex2/olex2c" && -f "${SRC_DIR}/olex2/olex2" ]] || \ + std::die 42 "olex2: unexpected archive layout - expected ${SRC_DIR}/olex2/{olex2,olex2c}" + rm -f "${zip}" } -pbuild::compile(){ +pbuild::compile() { : } pbuild::install() { - # Move all contents of the zip archive into PREFIX - mv "${SRC_DIR}/${P}/"* "${PREFIX}/" + local -r src="${SRC_DIR}/olex2" + + # 1. vendor tree, flat into $PREFIX (= BaseDir). Slow on /opt/psi: 12k files. + cp -a "${src}/." "${PREFIX}/" || std::die 42 "olex2: copying the vendor tree into ${PREFIX} failed" + + # 2. zip loses nothing, but make sure the ELF binaries are executable and + # everything is world-readable on the shared install + chmod 755 "${PREFIX}"/{olex2,olex2c,unirun,start,NoSpherA2,hart,hart_mpi} 2>/dev/null || true + chmod -R a+rX "${PREFIX}" || true + + # 3. never auto-update a shared, read-only install (vendor default is + # update=Always + ask_update=true, driven by unirun). Best effort - the + # launchers never call unirun anyway. + if [[ -f "${PREFIX}/usettings.dat" ]]; then + sed -i -e 's/^update=.*/update=Never/' -e 's/^ask_update=.*/ask_update=false/' \ + "${PREFIX}/usettings.dat" || true + fi + + # 4. launchers: $PREFIX/bin/olex2 (GUI) and $PREFIX/bin/olex2c (console). + # Two copies of one script (it dispatches on basename $0, so no symlinks). + # Only $PREFIX/bin goes on PATH - the raw binaries and the vendor + # 'start' (auto-updater) stay off it. + mkdir -p "${PREFIX}/bin" + install -m 755 "${BUILDBLOCK_DIR}/files/olex2-launcher" "${PREFIX}/bin/olex2" || \ + std::die 42 "olex2: installing bin/olex2 launcher failed" + install -m 755 "${BUILDBLOCK_DIR}/files/olex2-launcher" "${PREFIX}/bin/olex2c" || \ + std::die 42 "olex2: installing bin/olex2c launcher failed" + + # 5. what the modulefile / launchers rely on + local f + for f in olex2 olex2c cctbx/cctbx_build/lib lib/libpython3.8.so.1.0 lib/python3.8 util/pyUtil/initpy.py bin/olex2c; do + [[ -e "${PREFIX}/${f}" ]] || std::die 42 "olex2: ${PREFIX}/${f} missing after install - module would be broken" + done + echo "olex2: installed upstream build '$(cat "${PREFIX}/version.txt" 2>/dev/null)' (tag $(cat "${PREFIX}/olex2.tag" 2>/dev/null))" } diff --git a/olex2/files/config.yaml b/olex2/files/config.yaml index 9e9ce5e..258a254 100644 --- a/olex2/files/config.yaml +++ b/olex2/files/config.yaml @@ -4,9 +4,32 @@ olex2: defaults: group: MX overlay: base - relstage: stable - + relstage: unstable + urls: + # Official OlexSys Linux x86_64 archive (GUI + olex2c + cctbx + python3.8). + # ROLLING FILE: OlexSys refreshes /olex2-linux64.zip in place + # (same URL, new content, see "Modified" on + # https://secure.olex2.org/olex2-distro/1.5/). The module version is + # therefore -, which gives + # every snapshot its own distfiles name and sha256 below. + - url: https://secure.olex2.org/olex2-distro/${V_PKG}/olex2-linux64.zip + name: olex2-${V}-linux64.zip + unpacker: none # build/configure unzips (zip is not tar/7z) + shasums: + # sha256 of the 2026-09-08 drop of 1.5/olex2-linux64.zip (255161849 bytes, + # upstream MD5 b2d4a696854849ae586b96efd0b084ae, version.txt "SVN Revision No. 7665"). + # A mismatch after a future upstream refresh is EXPECTED: add a new version + # (README "Adding a new version") instead of editing this line. + olex2-1.5-7665-linux64.zip: a747d2a12d764448960cd45c2dbc8c57beb0a5403908abbf318e4c8e04dbf872 versions: + # 1.5: legacy Ra/RHEL8 module built by the previous recipe from a + # hand-downloaded olex2-linux64.zip committed next to build. Kept so the + # installed module stays listed; do NOT rebuild it with this recipe + # (no shasum, filename olex2-1.5-linux64.zip would fetch today's rolling zip). 1.5: config: relstage: stable + # Current snapshot, RHEL9 target: GUI + olex2c from the same archive. + 1.5-7665: + config: + relstage: unstable diff --git a/olex2/files/olex2-launcher b/olex2/files/olex2-launcher new file mode 100644 index 0000000..14dbfd0 --- /dev/null +++ b/olex2/files/olex2-launcher @@ -0,0 +1,47 @@ +#!/bin/bash +# Olex2 launcher, installed by the MX Pmodule as BOTH +# $PREFIX/bin/olex2 (GUI) +# $PREFIX/bin/olex2c (console / "headless" Olex2) +# One script, two copies: it dispatches on its own file name. +# +# It reproduces the environment of the vendor "start" script that ships in +# olex2-linux64.zip, but deliberately does NOT run the "unirun" auto-updater: +# /opt/psi/MX is shared and read-only for users, updates are done by +# rebuilding the module (see README.md, "Adding a new version"). + +set -u + +# $PREFIX/bin/ -> base = $PREFIX (= Olex2 BaseDir(): olex2, olex2c, +# cctbx/, lib/, ilib/, util/, etc/ all live there, vendor layout untouched) +here="$(cd -P "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" +base="${here%/bin}" +exe="$(basename "$0")" + +case "$exe" in + olex2|olex2c) ;; + *) echo "olex2-launcher: must be invoked as 'olex2' or 'olex2c', not '$exe'" >&2; exit 2 ;; +esac +[[ -x "$base/$exe" ]] || { echo "olex2-launcher: $base/$exe is missing or not executable (broken install?)" >&2; exit 1; } + +# --- vendor 'start' environment ------------------------------------------ +export OLEX2_CCTBX_DIR="$base/cctbx" +# cctbx sets signal handlers for FPE and does not handle them +export BOOST_ADAPTBX_FPE_DEFAULT=1 +export BOOST_ADAPTBX_SIGNALS_DEFAULT=1 +export OLEX2_GL_STEREO=FALSE +export OLEX2_GL_MULTISAMPLE=FALSE +export OLEX2_GL_DEPTH_BITS=16 +export LD_LIBRARY_PATH="$base/lib:$base/ilib:$OLEX2_CCTBX_DIR/cctbx_build/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" +# embedded CPython 3.8 (lib/python3.8, lib/libpython3.8.so.1.0) +export PYTHONHOME="$base" +# a PYTHONPATH inherited from another module/conda env would be imported by +# the embedded 3.8 interpreter and break it - never let it leak in +unset PYTHONPATH + +if [[ "$exe" == "olex2" ]]; then + # GUI only: software OpenGL avoids "Vulkan drivers missing" / GLX errors + # on display-less cluster nodes; users may override by pre-setting it to 0 + export LIBGL_ALWAYS_SOFTWARE="${LIBGL_ALWAYS_SOFTWARE:-1}" +fi + +exec "$base/$exe" "$@" diff --git a/olex2/modulefile b/olex2/modulefile index 729c843..fbf9680 100644 --- a/olex2/modulefile +++ b/olex2/modulefile @@ -1,21 +1,27 @@ -#%Module +#%Module1.0 -module-whatis "Olex2 is a free and comprehensive software package for determining, visualizing, and analyzing small-molecule crystal structures" +module-whatis "Olex2 - small-molecule structure solution, refinement and analysis: GUI (olex2) and console (olex2c)" module-url "https://www.olexsys.org/olex2" -module-license "https://www.olexsys.org/tags/licence/" +module-license "BSD - https://www.olexsys.org/olex2/docs/getting-started/installing-olex2/open-source/" module-maintainer "MX Data " module-help " -Olex2 is a free and comprehensive software package for determining, -visualizing, and analyzing small-molecule crystal structures. It features a -user-friendly, mouse-driven graphical interface to assist both chemists and -crystallographers with tasks like structure solution, refinement, and report -generation. The program seamlessly links all aspects of the crystallography -workflow into a single, intuitive package. +Olex2 is a free, open-source (BSD) package for determining, visualising and +analysing small-molecule crystal structures (solution, refinement with +olex2.refine/ShelXL, reports), built on cctbx/smtbx. + +This module installs the official OlexSys Linux x86_64 distribution 1:1 and +puts two launchers on PATH: + olex2 the wxWidgets GUI (needs an X display; uses software OpenGL + by default, export LIBGL_ALWAYS_SOFTWARE=0 to override) + olex2c the console / headless Olex2 - same engine, same commands + (reap, refine, solve, ...), no GUI, scriptable in batch jobs. +Both set LD_LIBRARY_PATH/PYTHONHOME for the bundled CPython 3.8 and cctbx +and never run the vendor auto-updater (shared read-only install). +Per-user data (DataDir) lives in ~/.olex2. The vendor Python API (olx, olex +modules) is only available from inside olex2/olex2c, not as a system python. " -# Use software OpenGL explicitly to avoid Vulkan drivers missing error -setenv LIBGL_ALWAYS_SOFTWARE 1 - -# Because the executable files need to be in the top directory -prepend-path PATH $PREFIX +# Launchers only: the raw binaries, the vendor 'start' script (auto-updater) +# and the private python live under $PREFIX root and stay off PATH. +prepend-path PATH $PREFIX/bin