diff --git a/.github/workflows/compile_madng.yml b/.github/workflows/compile_madng.yml index 5cbe89c..06e76a4 100644 --- a/.github/workflows/compile_madng.yml +++ b/.github/workflows/compile_madng.yml @@ -1,75 +1,293 @@ -# This workflow will install Python dependencies, run tests on a variety Python versions -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python - name: Make a MAD-NG Binary on: + push: workflow_dispatch: + # Allow release workflows to build and consume these exact binaries. + workflow_call: jobs: - build: - runs-on: ${{ matrix.os }} + build-linux: + runs-on: ubuntu-22.04 defaults: run: shell: bash - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest] steps: - - name: Checkout current repo - uses: actions/checkout@v6 - - - name: Clone another repo - uses: actions/checkout@v6 - with: - repository: MethodicalAcceleratorDesign/MAD-NG - path: MAD - submodules: recursive - - - name: Select Makefile - run: | - if [ "$RUNNER_OS" = "Linux" ]; then - cp MAD/src/Makefile.lxplus MAD/src/Makefile - elif [ "$RUNNER_OS" = "macOS" ]; then + - name: Checkout current repo + uses: actions/checkout@v4 + + - name: Clone MAD-NG + uses: actions/checkout@v4 + with: + repository: MethodicalAcceleratorDesign/MAD-NG + path: MAD + submodules: recursive + + - name: Build MAD-NG in Rocky Linux 9 + run: | + docker run --rm \ + -v "${GITHUB_WORKSPACE}:/work" \ + -w /work \ + rockylinux:9 \ + bash -lc ' + set -euo pipefail + + dnf install -y dnf-plugins-core + dnf config-manager --enable crb + dnf install -y \ + gcc gcc-c++ gcc-gfortran make cmake git \ + libquadmath-devel libquadmath-static \ + libgfortran-static libstdc++-static \ + diffutils findutils sed which tar gzip + + cp MAD/src/Makefile.lxplus MAD/src/Makefile + sed -i \ + -e '"'"'s#^CC[[:space:]]*=.*#CC=gcc#g'"'"' \ + -e '"'"'s#^CXX[[:space:]]*=.*#CXX=g++#g'"'"' \ + -e '"'"'s#-fdiagnostics-all-candidates##g'"'"' \ + MAD/src/Makefile || true + + mkdir -p MAD/tools + cp -f tools/build_madng_deps_linux.sh MAD/tools/ + chmod +x MAD/tools/build_madng_deps_linux.sh + (cd MAD && JOBS="$(nproc)" CC=gcc CXX=g++ FC=gfortran AR=ar RANLIB=ranlib ./tools/build_madng_deps_linux.sh) + + cd MAD/src + export CC=gcc CXX=g++ + gcc_libdir="$(gcc -print-libgcc-file-name | xargs dirname)" + export LIBRARY_PATH="${gcc_libdir}${LIBRARY_PATH:+:${LIBRARY_PATH}}" + export LDOPTIONS="-no-pie" + + make SHELL=/bin/bash \ + CC="${CC}" CXX="${CXX}" \ + LDOPTIONS="${LDOPTIONS:-}" + + chmod 755 mad + ' + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: mad-Linux + path: MAD/src/mad + + build-macos-x86_64: + runs-on: macos-15-intel + defaults: + run: + shell: bash + + steps: + - name: Checkout current repo + uses: actions/checkout@v4 + + - name: Clone MAD-NG + uses: actions/checkout@v4 + with: + repository: MethodicalAcceleratorDesign/MAD-NG + path: MAD + submodules: recursive + + - name: Install build tools + run: | + brew update + brew install gcc@12 make cmake automake autoconf libtool pkg-config curl + + - name: Prepare compiler shims + run: | + set -euo pipefail + + gcc_prefix="$(brew --prefix gcc@12)" + toolshim="${RUNNER_TEMP}/gcc-shim" + mkdir -p "${toolshim}" + + ln -sf "${gcc_prefix}/bin/gcc-12" "${toolshim}/gcc" + ln -sf "${gcc_prefix}/bin/g++-12" "${toolshim}/g++" + ln -sf "${gcc_prefix}/bin/gfortran-12" "${toolshim}/gfortran" + ln -sf "/usr/local/bin/make" "${toolshim}/make" + ln -sf "/usr/local/bin/cmake" "${toolshim}/cmake" + ln -sf "/usr/local/bin/pkg-config" "${toolshim}/pkg-config" + ln -sf "/usr/local/bin/libtool" "${toolshim}/libtool" + ln -sf "/usr/local/bin/ar" "${toolshim}/ar" + ln -sf "/usr/local/bin/ranlib" "${toolshim}/ranlib" + + { + echo "${toolshim}" + echo "/usr/local/bin" + echo "$(brew --prefix)/bin" + } >> "${GITHUB_PATH}" + + - name: Select MAD-NG macOS Makefile + run: | cp MAD/src/Makefile.macosx MAD/src/Makefile - else - echo "Unsupported OS: $RUNNER_OS" - exit 1 - fi - - - name: Install Linux dependencies - if: runner.os == 'Linux' - run: | - sudo apt-get update - sudo apt-get install -y \ - build-essential \ - gcc \ - g++ \ - make \ - - - name: Install macOS dependencies - if: runner.os == 'macOS' - run: | - brew update - brew install llvm libomp - - - name: Compile MAD-NG - working-directory: MAD/src - - run: | - if [ "$RUNNER_OS" = "macOS" ]; then - export PATH="$(brew --prefix llvm)/bin:$PATH" - export CC="$(brew --prefix llvm)/bin/clang" - export CXX="$(brew --prefix llvm)/bin/clang++" - export CPPFLAGS="-I$(brew --prefix libomp)/include" - export LDFLAGS="-L$(brew --prefix libomp)/lib -lomp" - fi - make -j - - - name: Upload it - uses: actions/upload-artifact@v7 - with: - name: executable-${{ runner.os }} - path: MAD/src/mad + python3 - <<'PY' + from pathlib import Path + + path = Path("MAD/src/Makefile") + text = path.read_text() + replacements = [ + ("CC := gcc-11", "CC := gcc"), + ("CXX := g++-11", "CXX := g++"), + ("FC := gfortran-11", "FC := gfortran"), + ("-fdiagnostics-all-candidates", ""), + ("-flto", ""), + ("-march=native", ""), + ("-mtune=native", ""), + ("-mfma", ""), + ] + for old, new in replacements: + text = text.replace(old, new) + path.write_text(text) + PY + + - name: Build MAD-NG dependencies (README order) + run: | + set -euo pipefail + + echo "== Toolchain ==" + which gcc + gcc --version + which g++ + g++ --version + which gfortran + gfortran --version + + export OS=macosx + export MACOSX_DEPLOYMENT_TARGET=11 + export JOBS="$(sysctl -n hw.ncpu)" + export CC=gcc + export CXX=g++ + export FC=gfortran + export AR=ar + export RANLIB=ranlib + + mkdir -p MAD/bin/macosx + + cd MAD/lib + echo "== MAD/lib contents (before download) ==" + ls -la + + echo "== LuaJIT (README.luajit) ==" + rm -rf luajit + git clone https://github.com/MethodicalAcceleratorDesign/LuaJIT.git luajit + cd luajit + git checkout mad-patch + git pull + make clean + make -j"${JOBS}" amalg PREFIX="$(pwd)" + make install PREFIX="$(pwd)" + cp -f src/libluajit.a ../../bin/macosx/ + cd .. + + echo "== LFS (README.lfs) ==" + rm -rf lfs + git clone https://github.com/MethodicalAcceleratorDesign/luafilesystem.git lfs + cd lfs + make clean || true + make lfs.a + cp -f liblfs.a ../../bin/macosx/ + cd .. + + echo "== LPEG (README.lpeg) ==" + rm -rf lpeg lpeg-1.1.0 lpeg-1.1.0.tar.gz + curl -fsSL -o lpeg-1.1.0.tar.gz http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-1.1.0.tar.gz + tar xvzf lpeg-1.1.0.tar.gz + ln -s lpeg-1.1.0 lpeg + cd lpeg + python3 - <<'PY' + from pathlib import Path + import re + + path = Path("makefile") + text = path.read_text() + text = re.sub(r"^LUADIR[ \t]*=.*$", "LUADIR = ../luajit/include/luajit-2.1", text, flags=re.MULTILINE) + text = re.sub(r"^LUALIB[ \t]*=.*$", "LUALIB = ../luajit/lib/libluajit-5.1.a", text, flags=re.MULTILINE) + text = re.sub(r"^COPT[ \t]*=.*$", "COPT = -O3 -DNDEBUG", text, flags=re.MULTILINE) + path.write_text(text) + PY + make clean || true + make -j"${JOBS}" CC="${CC:-gcc}" LUADIR="../luajit/include/luajit-2.1" lpvm.o lpcap.o lptree.o lpcode.o lpprint.o lpcset.o + ar -r liblpeg.a lpvm.o lpcap.o lptree.o lpcode.o lpprint.o lpcset.o + ranlib liblpeg.a + cp -f liblpeg.a ../../bin/macosx/ + cd .. + + echo "== LAPACK (README.lapack) ==" + rm -rf lapack + git clone https://github.com/Reference-LAPACK/lapack lapack + cd lapack + cp -f make.inc.example make.inc + python3 - <<'PY' + from pathlib import Path + import re + + path = Path("make.inc") + text = path.read_text() + for key in ("CFLAGS", "FFLAGS", "FFLAGS_NOOPT"): + text = re.sub(rf"^({key}[ \t]*=[ \t]*)(?!-fPIC\b)", r"\1-fPIC ", text, flags=re.MULTILINE) + path.write_text(text) + PY + make clean || true + make -j"${JOBS}" blaslib + make -j"${JOBS}" lapacklib + cp -f librefblas.a ../../bin/macosx/ + cp -f liblapack.a ../../bin/macosx/ + cd .. + + echo "== FFTW3 (README.fftw3) ==" + rm -rf fftw3 fftw-3.3.10 fftw-3.3.10.tar.gz + curl -fsSL -o fftw-3.3.10.tar.gz ftp://ftp.fftw.org/pub/fftw/fftw-3.3.10.tar.gz + tar xvzf fftw-3.3.10.tar.gz + ln -s fftw-3.3.10 fftw3 + cd fftw3 + make clean || true + ./configure --disable-shared + make -j"${JOBS}" + cp -f .libs/libfftw3.a ../../bin/macosx/ + cd .. + + echo "== NFFT3 (README.nfft3) ==" + rm -rf nfft3 nfft-3.5.3 nfft-3.5.3.tar.gz + curl -fsSL -o nfft-3.5.3.tar.gz https://www-user.tu-chemnitz.de/~potts/nfft/download/nfft-3.5.3.tar.gz + tar xvzf nfft-3.5.3.tar.gz + ln -s nfft-3.5.3 nfft3 + cd nfft3 + make clean || true + ./configure --enable-all --disable-shared \ + --with-fftw3="$(pwd)"/../fftw3 \ + --with-fftw3-libdir="$(pwd)"/../fftw3/.libs \ + --with-fftw3-includedir="$(pwd)"/../fftw3/api + make -j"${JOBS}" + cp -f .libs/libnfft3.a ../../bin/macosx/ + cd .. + + echo "== NLOPT (README.nlopt) ==" + rm -rf nlopt + git clone https://github.com/stevengj/nlopt nlopt + cd nlopt + rm -rf build + mkdir -p build + cd build + cmake -DBUILD_SHARED_LIBS=OFF -DNLOPT_CXX=OFF .. + make -j"${JOBS}" + cp -f libnlopt.a ../../../bin/macosx/ + + - name: Compile MAD-NG + working-directory: MAD/src + run: | + echo "== Toolchain ==" + which gcc + gcc --version + which g++ + g++ --version + which gfortran + gfortran --version + + make SHELL=/bin/bash CC=gcc CXX=g++ FC=gfortran + + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: mad-macOS-x86_64 + path: MAD/src/mad diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 7e55230..e974667 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -16,7 +16,15 @@ permissions: contents: read jobs: + # Set the repository variable PYMADNG_BUILD_MADNG_FROM_SOURCE to "false" + # to temporarily restore the legacy pre-built binary download path below. + compile-madng: + if: ${{ vars.PYMADNG_BUILD_MADNG_FROM_SOURCE != 'false' }} + uses: ./.github/workflows/compile_madng.yml + deploy: + needs: compile-madng + if: ${{ always() && (needs.compile-madng.result == 'success' || needs.compile-madng.result == 'skipped') }} runs-on: ubuntu-latest # Specifying a GitHub environment is optional, but strongly encouraged environment: release @@ -34,11 +42,30 @@ jobs: run: | python -m pip install --upgrade pip pip install build - - name: Get MAD Binaries + - name: Download compiled Linux MAD-NG + if: ${{ vars.PYMADNG_BUILD_MADNG_FROM_SOURCE != 'false' }} + uses: actions/download-artifact@v5 + with: + name: mad-Linux + path: build/mad-Linux + - name: Download compiled macOS MAD-NG + if: ${{ vars.PYMADNG_BUILD_MADNG_FROM_SOURCE != 'false' }} + uses: actions/download-artifact@v5 + with: + name: mad-macOS-x86_64 + path: build/mad-Darwin + - name: Add compiled MAD-NG binaries to the package + if: ${{ vars.PYMADNG_BUILD_MADNG_FROM_SOURCE != 'false' }} + run: | + mkdir -p src/pymadng/bin + install -m 755 build/mad-Linux/mad src/pymadng/bin/mad_Linux + install -m 755 build/mad-Darwin/mad src/pymadng/bin/mad_Darwin + - name: Download pre-built MAD-NG binaries (legacy fallback) + if: ${{ vars.PYMADNG_BUILD_MADNG_FROM_SOURCE == 'false' }} run: | - mkdir src/pymadng/bin - curl https://madx.web.cern.ch/releases/madng/1.1/mad-linux-1.1.14 -o src/pymadng/bin/mad_Linux - curl https://madx.web.cern.ch/releases/madng/1.1/mad-macos-1.1.14 -o src/pymadng/bin/mad_Darwin + mkdir -p src/pymadng/bin + curl --fail --location https://madx.web.cern.ch/releases/madng/1.1/mad-linux-1.1.14 -o src/pymadng/bin/mad_Linux + curl --fail --location https://madx.web.cern.ch/releases/madng/1.1/mad-macos-1.1.14 -o src/pymadng/bin/mad_Darwin chmod +x src/pymadng/bin/mad_Linux src/pymadng/bin/mad_Darwin - name: Build package run: python -m build diff --git a/tools/README.deps.md b/tools/README.deps.md new file mode 100644 index 0000000..f5b424d --- /dev/null +++ b/tools/README.deps.md @@ -0,0 +1,41 @@ +# Building MAD-NG dependencies + +This directory contains the Linux dependency builder used by the MAD-NG +compile workflow. It builds static third-party libraries from source; it does +not build or publish `pymadng` itself. + +## Release route + +Publishing a GitHub release runs these workflows in order: + +1. `python-publish.yml` calls `compile_madng.yml`. +2. The Linux job copies this script into the checked-out MAD-NG source and runs + it. The macOS job builds its dependencies directly in the workflow. +3. Both jobs compile MAD-NG and upload a `mad` artifact. +4. The publish job installs the artifacts as + `src/pymadng/bin/mad_Linux` and `src/pymadng/bin/mad_Darwin`, builds the + Python distributions, and publishes them to PyPI. + +To temporarily use the old pre-built-download route, set the GitHub repository +variable `PYMADNG_BUILD_MADNG_FROM_SOURCE` to `false`. Delete the variable, or +set it to any other value, to compile from source again. + +## Manual Linux route + +Run the script from the root of a MAD-NG checkout: + +```bash +JOBS="$(nproc)" CC=gcc CXX=g++ FC=gfortran \ + AR=ar RANLIB=ranlib ./tools/build_madng_deps_linux.sh +``` + +If an option is omitted, the script prompts for it and suggests a default. +It downloads sources into `lib/` and writes these archives to `bin/linux/`: + +- `libluajit.a`, `liblfs.a`, `liblpeg.a` +- `liblapack.a`, `librefblas.a` +- `libfftw3.a`, `libnfft3.a`, `libnlopt.a` + +Source versions and repositories can be overridden with `LUAJIT_REPO`, +`LUAJIT_REF`, `LFS_REPO`, `LFS_REF`, `LPEG_VERSION`, `FFTW_VERSION`, +`NFFT_VERSION`, `NLOPT_REF`, and `LAPACK_REF`. diff --git a/tools/build_madng_deps_linux.sh b/tools/build_madng_deps_linux.sh new file mode 100755 index 0000000..31c75c6 --- /dev/null +++ b/tools/build_madng_deps_linux.sh @@ -0,0 +1,325 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Builds MAD-NG third-party *static* libraries into: ./bin/linux +# +# This script is intentionally "fill-in friendly": override variables via env +# or interactively when prompted. +# +# Example: +# ./tools/build_madng_deps_linux.sh +# +# Outputs (matching historical `my_lib`): +# bin/linux/libluajit.a +# bin/linux/liblfs.a +# bin/linux/liblpeg.a +# bin/linux/liblapack.a +# bin/linux/librefblas.a +# bin/linux/libfftw3.a +# bin/linux/libnfft3.a +# bin/linux/libnlopt.a + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +OS_TAG="linux" +BIN_DIR="${ROOT}/bin/${OS_TAG}" +LIB_DIR="${ROOT}/lib" + +JOBS="${JOBS:-}" +CC="${CC:-}" +CXX="${CXX:-}" +FC="${FC:-}" +AR="${AR:-}" +RANLIB="${RANLIB:-}" + +LUAJIT_REF="${LUAJIT_REF:-mad-patch}" +LUAJIT_REPO="${LUAJIT_REPO:-https://github.com/MethodicalAcceleratorDesign/LuaJIT.git}" + +LFS_REF="${LFS_REF:-}" +LFS_REPO="${LFS_REPO:-https://github.com/MethodicalAcceleratorDesign/luafilesystem.git}" + +LPEG_VERSION="${LPEG_VERSION:-1.1.0}" +FFTW_VERSION="${FFTW_VERSION:-3.3.10}" +NFFT_VERSION="${NFFT_VERSION:-3.5.3}" +NLOPT_REF="${NLOPT_REF:-v2.9.1}" +LAPACK_REF="${LAPACK_REF:-v3.12.1}" + +prompt_default() { + local var_name="$1" + local prompt="$2" + local default="$3" + local current="${!var_name:-}" + + if [[ -n "${current}" ]]; then + return 0 + fi + + local reply="" + read -r -p "${prompt} [${default}]: " reply || true + if [[ -z "${reply}" ]]; then + reply="${default}" + fi + printf -v "${var_name}" "%s" "${reply}" +} + +need_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "Missing required command: $1" >&2 + exit 1 + } +} + +fetch_tarball() { + local url="$1" + local out="$2" + if [[ -f "${out}" ]]; then + return 0 + fi + if command -v curl >/dev/null 2>&1; then + curl -L --fail "${url}" -o "${out}" + else + need_cmd wget + wget -O "${out}" "${url}" + fi +} + +ensure_dirs() { + mkdir -p "${BIN_DIR}" + mkdir -p "${LIB_DIR}" +} + +build_luajit() { + echo "== LuaJIT (${LUAJIT_REF}) ==" + need_cmd git + need_cmd make + + local dir="${LIB_DIR}/luajit" + if [[ ! -d "${dir}/.git" ]]; then + git clone "${LUAJIT_REPO}" "${dir}" + fi + ( cd "${dir}" + git fetch --all --tags + git checkout "${LUAJIT_REF}" + git pull --ff-only || true + make clean + make -j"${JOBS}" amalg PREFIX="$(pwd)" + make install PREFIX="$(pwd)" + cp -f src/libluajit.a "${BIN_DIR}/libluajit.a" + ) +} + +build_lfs() { + echo "== LuaFileSystem ${LFS_REF:+(${LFS_REF})} ==" + need_cmd git + need_cmd make + + local dir="${LIB_DIR}/lfs" + if [[ ! -d "${dir}/.git" ]]; then + git clone "${LFS_REPO}" "${dir}" + fi + ( cd "${dir}" + git fetch --all --tags + git pull --ff-only || true + if [[ -n "${LFS_REF}" ]]; then + git checkout "${LFS_REF}" || git checkout -B "${LFS_REF}" "origin/${LFS_REF}" + fi + make clean || true + make -j"${JOBS}" AR="${AR}" lfs.a + cp -f liblfs.a "${BIN_DIR}/liblfs.a" + ) +} + +build_lpeg() { + echo "== LPeg (${LPEG_VERSION}) ==" + need_cmd make + need_cmd sed + need_cmd ar + + local tar="lpeg-${LPEG_VERSION}.tar.gz" + local url="http://www.inf.puc-rio.br/~roberto/lpeg/${tar}" + local tar_path="${LIB_DIR}/${tar}" + local src_dir="${LIB_DIR}/lpeg-${LPEG_VERSION}" + local dir="${LIB_DIR}/lpeg" + + if [[ ! -d "${src_dir}" ]]; then + fetch_tarball "${url}" "${tar_path}" + ( cd "${LIB_DIR}" && tar xzf "${tar}" ) + fi + if [[ ! -e "${dir}" ]]; then + ln -s "lpeg-${LPEG_VERSION}" "${dir}" + fi + + local luajit_inc="${LIB_DIR}/luajit/include/luajit-2.1" + local luajit_lib="${LIB_DIR}/luajit/lib/libluajit-5.1.a" + if [[ ! -f "${luajit_lib}" ]]; then + echo "LuaJIT static library not found at ${luajit_lib}" >&2 + echo "Run LuaJIT step first (or adjust paths)." >&2 + exit 1 + fi + + ( cd "${dir}" + if [[ ! -f makefile.mad.orig ]]; then + cp -f makefile makefile.mad.orig + else + cp -f makefile.mad.orig makefile + fi + sed -i -E \ + -e "s|^[#[:space:]]*LUADIR[[:space:]]*=.*|LUADIR = ${luajit_inc}|" \ + -e "s|^[#[:space:]]*LUALIB[[:space:]]*=.*|LUALIB = ${luajit_lib}|" \ + -e "s|^[#[:space:]]*COPT[[:space:]]*=.*|COPT = -O3|" \ + makefile + make clean || true + make -j"${JOBS}" CC="${CC}" LUADIR="${luajit_inc}" lpvm.o lpcap.o lptree.o lpcode.o lpprint.o lpcset.o + "${AR}" -r liblpeg.a lpvm.o lpcap.o lptree.o lpcode.o lpprint.o lpcset.o + "${RANLIB}" liblpeg.a + cp -f liblpeg.a "${BIN_DIR}/liblpeg.a" + ) +} + +build_lapack() { + echo "== LAPACK (${LAPACK_REF}) ==" + need_cmd git + need_cmd make + + local dir="${LIB_DIR}/lapack" + if [[ ! -d "${dir}/.git" ]]; then + git clone https://github.com/Reference-LAPACK/lapack "${dir}" + fi + ( cd "${dir}" + git fetch --all --tags + git checkout "${LAPACK_REF}" + cp -f make.inc.example make.inc + # Ensure PIC (LAPACK can be linked into shared objects, depending on MAD build) + sed -i \ + -e 's/^\(CFLAGS[[:space:]]*=[[:space:]]*.*\)$/\1 -fPIC/' \ + -e 's/^\(FFLAGS[[:space:]]*=[[:space:]]*.*\)$/\1 -fPIC/' \ + -e 's/^\(FFLAGS_NOOPT[[:space:]]*=[[:space:]]*.*\)$/\1 -fPIC/' \ + make.inc || true + make clean + make -j"${JOBS}" blaslib + make -j"${JOBS}" lapacklib + cp -f librefblas.a "${BIN_DIR}/librefblas.a" + cp -f liblapack.a "${BIN_DIR}/liblapack.a" + ) +} + +build_fftw3() { + echo "== FFTW3 (${FFTW_VERSION}) ==" + need_cmd make + + local tar="fftw-${FFTW_VERSION}.tar.gz" + local url="https://fftw.org/${tar}" + local tar_path="${LIB_DIR}/${tar}" + local src_dir="${LIB_DIR}/fftw-${FFTW_VERSION}" + local dir="${LIB_DIR}/fftw3" + + if [[ ! -d "${src_dir}" ]]; then + fetch_tarball "${url}" "${tar_path}" + ( cd "${LIB_DIR}" && tar xzf "${tar}" ) + fi + if [[ ! -e "${dir}" ]]; then + ln -s "fftw-${FFTW_VERSION}" "${dir}" + fi + + ( cd "${dir}" + ./configure --disable-shared CC="${CC}" + make clean || true + make -j"${JOBS}" + cp -f .libs/libfftw3.a "${BIN_DIR}/libfftw3.a" + ) +} + +build_nfft3() { + echo "== NFFT3 (${NFFT_VERSION}) ==" + need_cmd make + + local tar="nfft-${NFFT_VERSION}.tar.gz" + local url="https://www-user.tu-chemnitz.de/~potts/nfft/download/${tar}" + local tar_path="${LIB_DIR}/${tar}" + local src_dir="${LIB_DIR}/nfft-${NFFT_VERSION}" + local dir="${LIB_DIR}/nfft3" + + if [[ ! -d "${src_dir}" ]]; then + fetch_tarball "${url}" "${tar_path}" + ( cd "${LIB_DIR}" && tar xzf "${tar}" ) + fi + if [[ ! -e "${dir}" ]]; then + ln -s "nfft-${NFFT_VERSION}" "${dir}" + fi + + ( cd "${dir}" + make distclean || true + ./configure --disable-shared \ + --with-fftw3="$(pwd)/../fftw3" \ + --with-fftw3-libdir="$(pwd)/../fftw3/.libs" \ + --with-fftw3-includedir="$(pwd)/../fftw3/api" \ + CC="${CC}" + # Only build the library — strip applications/examples/tests from SUBDIRS. + sed -i 's/^\(SUBDIRS[[:space:]]*=\).*/\1 include kernel/' Makefile + make -j"${JOBS}" + cp -f .libs/libnfft3.a "${BIN_DIR}/libnfft3.a" + ) +} + +build_nlopt() { + echo "== NLOpt (${NLOPT_REF}) ==" + need_cmd git + need_cmd cmake + need_cmd make + + local dir="${LIB_DIR}/nlopt" + if [[ ! -d "${dir}/.git" ]]; then + git clone https://github.com/stevengj/nlopt "${dir}" + fi + + ( cd "${dir}" + git fetch --all --tags + git checkout "${NLOPT_REF}" + rm -rf build + mkdir -p build + cd build + cmake -DBUILD_SHARED_LIBS=OFF -DNLOPT_CXX=OFF -DCMAKE_C_COMPILER="${CC}" \ + -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. + make -j"${JOBS}" + cp -f libnlopt.a "${BIN_DIR}/libnlopt.a" + ) +} + +main() { + ensure_dirs + + prompt_default JOBS "Parallel jobs (make -j)" "$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" + prompt_default CC "C compiler (CC)" "gcc" + prompt_default CXX "C++ compiler (CXX)" "g++" + prompt_default FC "Fortran compiler (FC)" "gfortran" + prompt_default AR "Archiver (AR)" "ar" + prompt_default RANLIB "RANLIB" "ranlib" + + need_cmd "${CC}" + need_cmd "${FC}" + need_cmd "${AR}" + need_cmd "${RANLIB}" + need_cmd tar + + if ! command -v rg >/dev/null 2>&1; then + echo "Tip: install ripgrep ('rg') for better diagnostics; continuing without it." + fi + + echo "Root: ${ROOT}" + echo "Lib sources: ${LIB_DIR}" + echo "Output: ${BIN_DIR}" + echo + + build_luajit + build_lfs + build_lpeg + build_lapack + build_fftw3 + build_nfft3 + build_nlopt + + echo + echo "Done. Built libraries in: ${BIN_DIR}" + ls -1 "${BIN_DIR}" +} + +main "$@"