diff --git a/.gitignore b/.gitignore index 39bc406..1eb7ac1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ /build*/ /compile_commands.json +# Generated paclet archive (produced by `make create_paclet_archive`) +/paclet_archive/ + # Benchmark results - exclude intermediate files /benchmark_results/*/*.csv /benchmark_results/*/per_benchmark/ diff --git a/README.md b/README.md index c91173b..31d2a7b 100644 --- a/README.md +++ b/README.md @@ -31,12 +31,39 @@ The paclet name is `WolframInstitute/HypergraphRewriteEngine`; its exported cont ### Building from Source +**One command, every platform this machine can target** (recommended). It +auto-installs the cross-toolchains it needs, builds each platform library, and +assembles the distributable archive: + +```bash +./build_all_platforms.sh +``` + +Outputs: + +| Artifact | Location | +|----------|----------| +| Per-platform loadable libraries | `paclet/LibraryResources//` | +| Distributable archive | `paclet_archive/WolframInstitute__HypergraphRewriteEngine-.paclet` | + +Platforms the current host architecture cannot target (e.g. Windows-ARM64 on +macOS, which needs the Windows SDK) are reported as *skipped*, not failures — +see [Cross-Compilation](#cross-compilation). + +**Single platform only** (just the library for the current machine): + ```bash -mkdir build_linux && cd build_linux +mkdir build && cd build cmake .. -DBUILD_MATHEMATICA_PACLET=ON -make -j32 paclet +make -j paclet # -> paclet/LibraryResources// +make create_paclet_archive # -> paclet_archive/*.paclet ``` +The build auto-detects a Wolfram/Mathematica install +(`/Applications/Wolfram.app`, `Mathematica.app`, or `Wolfram Engine.app`; +their `~/Applications` equivalents; standard Linux/Windows locations). Override +with `MATHEMATICA_HOME=/path/to/Wolfram/Contents` if it lives elsewhere. + ## Usage ### Mathematica @@ -116,29 +143,38 @@ The paclet includes native libraries for: ## Cross-Compilation -Build all 6 platforms from Linux: +`./build_all_platforms.sh` builds every target the host can reach from a +declarative toolchain *schema*. Each target lists the toolchain it needs and how +to install it; the script auto-installs missing toolchains, skips targets the +host architecture cannot support, and only fails on a genuine build error +(`--no-install` to opt out of installs, `--help` for all options). -```bash -./build_all_platforms.sh -``` +What each host can build: -Required packages (Ubuntu/Debian): +| Target | Toolchain | macOS host | Linux host | +|--------|-----------|:----------:|:----------:| +| macOS x86-64 / ARM64 | Apple clang (native) / OSXCross | ✅ native | OSXCross | +| Linux x86-64 / ARM64 | GCC cross (`messense` tap / `apt`) | ✅ auto | ✅ native + cross | +| Windows x86-64 | MinGW-w64 (`brew` / `apt`) | ✅ auto | ✅ auto | +| Windows ARM64 | Clang + Windows SDK + MSVC libs | ⏭️ skipped | ⏭️ skipped | + +Windows ARM64 needs the Windows SDK and MSVC import libraries, which only exist +on a Windows/WSL host — it is skipped elsewhere rather than aborting the run. + +The toolchains are installed automatically, but for reference: ```bash -sudo apt install \ - cmake build-essential \ +# macOS (Homebrew) +brew install mingw-w64 +brew install messense/macos-cross-toolchains/x86_64-unknown-linux-gnu +brew install messense/macos-cross-toolchains/aarch64-unknown-linux-gnu + +# Linux (Ubuntu/Debian) +sudo apt install cmake build-essential \ gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \ - gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 \ - clang-22 lld-22 + gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 ``` -| Target | Toolchain | -|--------|-----------| -| Linux ARM64 | `gcc-aarch64-linux-gnu` | -| Windows x86-64 | MinGW-w64 | -| Windows ARM64 | Clang 22 + LLD | -| macOS | [OSXCross](https://github.com/tpoechtrager/osxcross) | - See [CROSS_COMPILATION.md](CROSS_COMPILATION.md) for detailed setup. ## Project Structure diff --git a/build_all_platforms.sh b/build_all_platforms.sh index dcd7d90..78aae85 100755 --- a/build_all_platforms.sh +++ b/build_all_platforms.sh @@ -1,322 +1,298 @@ -#!/bin/bash -# Build hypergraph rewriting library for all platforms -# Creates a complete multi-platform paclet - -set -e # Exit on error +#!/usr/bin/env bash +# Build the Hypergraph Rewriting paclet libraries for every platform this host +# can target, then assemble the multi-platform paclet archive. +# +# Design goals: +# * Declarative target SCHEMA - every platform lists the toolchain it needs, +# how to auto-install that toolchain, and how to configure its build. +# * Fully automatic - missing-but-installable toolchains are installed for you +# (disable with --no-install). +# * No "crap-shoot" failures - a platform the current host architecture simply +# cannot target (e.g. Windows-ARM64 on macOS, which needs the Windows SDK) is +# SKIPPED with a clear reason. The run only FAILS when a build we actually +# attempted errors out. +# +# Run `./build_all_platforms.sh --help` for options. + +set -uo pipefail # deliberately NOT -e: per-target errors are handled inline PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$PROJECT_ROOT" -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -echo -e "${GREEN}=== Building Hypergraph Rewriting Library for All Platforms ===${NC}\n" +# Colors +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m' -# Clean existing library files to ensure fresh builds -echo -e "${GREEN}Cleaning existing library files...${NC}" -rm -f paclet/LibraryResources/*/*.so paclet/LibraryResources/*/*.dll paclet/LibraryResources/*/*.dylib -echo -e "${GREEN}✓ Cleaned${NC}\n" +HOST_OS="$(uname -s)" # Darwin | Linux +HOST_ARCH="$(uname -m)" # arm64 | x86_64 | aarch64 +BUILD_JOBS="${BUILD_JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}" +TOOLCHAINS="$PROJECT_ROOT/cmake/toolchains" -# Configuration -BUILD_LINUX=true -BUILD_WINDOWS=true -BUILD_MACOS=true -BUILD_JOBS=${BUILD_JOBS:-$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)} +AUTO_INSTALL=true +WANT_LINUX=true; WANT_WINDOWS=true; WANT_MACOS=true +WANT_ARCHIVE=true -# Parse arguments for arg in "$@"; do - case $arg in - --linux-only) - BUILD_LINUX=true - BUILD_WINDOWS=false - BUILD_MACOS=false - ;; - --windows-only) - BUILD_LINUX=false - BUILD_WINDOWS=true - BUILD_MACOS=false - ;; - --macos-only) - BUILD_LINUX=false - BUILD_WINDOWS=false - BUILD_MACOS=true - ;; - --no-linux) - BUILD_LINUX=false - ;; - --no-windows) - BUILD_WINDOWS=false - ;; - --no-macos) - BUILD_MACOS=false - ;; + case "$arg" in + --no-install) AUTO_INSTALL=false ;; + --no-archive) WANT_ARCHIVE=false ;; + --linux-only) WANT_LINUX=true; WANT_WINDOWS=false; WANT_MACOS=false ;; + --windows-only) WANT_LINUX=false; WANT_WINDOWS=true; WANT_MACOS=false ;; + --macos-only) WANT_LINUX=false; WANT_WINDOWS=false; WANT_MACOS=true ;; + --no-linux) WANT_LINUX=false ;; + --no-windows) WANT_WINDOWS=false ;; + --no-macos) WANT_MACOS=false ;; --help) - echo "Usage: $0 [OPTIONS]" - echo "" - echo "Build for all platforms (default) or specific platforms:" - echo " --linux-only Build only Linux" - echo " --windows-only Build only Windows" - echo " --macos-only Build only macOS" - echo " --no-linux Skip Linux build" - echo " --no-windows Skip Windows build" - echo " --no-macos Skip macOS build" - echo "" - echo "Environment variables:" - echo " BUILD_JOBS Number of parallel jobs (default: auto-detect)" - echo " OSXCROSS_ROOT Path to OSXCross (default: ~/osxcross)" - echo "" - echo "Dependencies (Ubuntu/Debian):" - echo " sudo apt install cmake build-essential \\" - echo " gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \\" - echo " gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 \\" - echo " clang lld" - echo "" - echo " For macOS, also install OSXCross: https://github.com/tpoechtrager/osxcross" - exit 0 - ;; - esac -done + cat <<'EOF' +Usage: ./build_all_platforms.sh [OPTIONS] -# Check prerequisites -check_prerequisite() { - local name=$1 - local command=$2 - local install_hint=$3 +Builds every platform library this host can target, auto-installing the +required cross-toolchains, then assembles the .paclet archive. - if ! command -v "$command" &> /dev/null; then - echo -e "${YELLOW}Warning: $name not found${NC}" - echo -e " Install: $install_hint" - return 1 - fi - return 0 -} +Platform selection: + --linux-only / --windows-only / --macos-only Build only that OS family + --no-linux / --no-windows / --no-macos Skip that OS family -echo -e "${GREEN}Checking prerequisites...${NC}" -check_prerequisite "CMake" "cmake" "sudo apt install cmake (Linux) or brew install cmake (macOS)" +Behaviour: + --no-install Do not auto-install missing toolchains (skip instead) + --no-archive Build libraries only; do not assemble the .paclet archive -# Linux build -if $BUILD_LINUX; then - echo -e "\n${GREEN}=== Building for Linux (x86_64) ===${NC}" - mkdir -p build_linux - cd build_linux +Environment: + BUILD_JOBS Parallel build jobs (default: auto-detect) - # Always configure to ensure correct build settings - cmake .. \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_MATHEMATICA_PACLET=ON \ - || { echo -e "${RED}Linux CMake configuration failed${NC}"; exit 1; } +Toolchains are described by a schema inside this script; platforms whose +toolchain cannot exist on this host architecture are reported as SKIPPED, +not as failures. +EOF + exit 0 ;; + *) echo -e "${YELLOW}Unknown option: $arg (try --help)${NC}" ;; + esac +done - make -j"$BUILD_JOBS" paclet \ - || { echo -e "${RED}Linux build failed${NC}"; exit 1; } +# --------------------------------------------------------------------------- +# Toolchain installer helpers (macOS / Homebrew) +# --------------------------------------------------------------------------- +brew_install() { + command -v brew >/dev/null 2>&1 || { echo "Homebrew not available"; return 1; } + brew install "$@" +} - echo -e "${GREEN}✓ Linux build complete${NC}" - cd "$PROJECT_ROOT" -fi +# Homebrew's messense cross-toolchains expose binaries as -unknown-linux-gnu-*, +# but the CMake toolchain (and convention) expect the -linux-gnu-* prefix. +# Symlink the short names into the Homebrew bin so detection just works. +link_linux_cross() { + local arch="$1" formula="$2" keg dest t + keg="$(brew --prefix "$formula" 2>/dev/null)" || return 1 + dest="$(brew --prefix)/bin" + for t in gcc g++ ar ranlib; do + [ -e "$keg/bin/${arch}-linux-gnu-$t" ] && ln -sf "$keg/bin/${arch}-linux-gnu-$t" "$dest/${arch}-linux-gnu-$t" + done +} -# Linux ARM64 build -if $BUILD_LINUX; then - echo -e "\n${GREEN}=== Building for Linux (ARM64) ===${NC}" +# --------------------------------------------------------------------------- +# Target SCHEMA +# Parallel arrays, one entry per platform. (Bash 3.2 compatible - no assoc arrays.) +# NAME platform directory under paclet/LibraryResources +# DIR build directory +# OUT artifact (relative to paclet/LibraryResources) used to confirm success +# PROBE shell expression, true when the toolchain is present +# INSTALL command to auto-install the toolchain ("" = not auto-installable here) +# CONFIG cmake configure command (run from inside DIR) +# HINT human reason / requirement, shown when a target is skipped +# --------------------------------------------------------------------------- +T_NAME=(); T_DIR=(); T_OUT=(); T_PROBE=(); T_INSTALL=(); T_CONFIG=(); T_HINT=() +add_target() { + T_NAME+=("$1"); T_DIR+=("$2"); T_OUT+=("$3"); T_PROBE+=("$4") + T_INSTALL+=("$5"); T_CONFIG+=("$6"); T_HINT+=("$7") +} - if ! check_prerequisite "GCC-ARM64" "aarch64-linux-gnu-gcc" "sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu (Linux)"; then - echo -e "${YELLOW}Skipping Linux ARM64 build - ARM64 cross-compiler not available${NC}" - else - mkdir -p build_linux_arm64 - cd build_linux_arm64 - - # Always configure to ensure correct build settings - cmake .. \ - -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/linux-cross.cmake \ - -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_MATHEMATICA_PACLET=ON \ - || { echo -e "${RED}Linux ARM64 CMake configuration failed${NC}"; exit 1; } - - make -j"$BUILD_JOBS" paclet \ - || { echo -e "${RED}Linux ARM64 build failed${NC}"; exit 1; } - - echo -e "${GREEN}✓ Linux ARM64 build complete${NC}" - cd "$PROJECT_ROOT" - fi -fi +COMMON="-DCMAKE_BUILD_TYPE=Release -DBUILD_MATHEMATICA_PACLET=ON" + +define_targets_darwin() { + # Native macOS builds - Apple clang targets either arch from the universal SDK. + add_target "MacOSX-ARM64" "build_macos_arm64" "MacOSX-ARM64/libHypergraphRewriting.dylib" \ + "true" "" \ + "cmake .. $COMMON -DCMAKE_OSX_ARCHITECTURES=arm64" \ + "native (Apple clang)" + + add_target "MacOSX-x86-64" "build_macos_x64" "MacOSX-x86-64/libHypergraphRewriting.dylib" \ + "true" "" \ + "cmake .. $COMMON -DCMAKE_OSX_ARCHITECTURES=x86_64" \ + "native (Apple clang, x86_64 slice of universal SDK)" + + # Linux cross-toolchains via the messense Homebrew tap. + add_target "Linux-x86-64" "build_linux_x64" "Linux-x86-64/libHypergraphRewriting.so" \ + "command -v x86_64-linux-gnu-gcc" \ + "brew_install messense/macos-cross-toolchains/x86_64-unknown-linux-gnu && link_linux_cross x86_64 x86_64-unknown-linux-gnu" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/linux-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=x86_64" \ + "Linux x86_64 cross-toolchain (brew messense tap)" + + add_target "Linux-ARM64" "build_linux_arm64" "Linux-ARM64/libHypergraphRewriting.so" \ + "command -v aarch64-linux-gnu-gcc" \ + "brew_install messense/macos-cross-toolchains/aarch64-unknown-linux-gnu && link_linux_cross aarch64 aarch64-unknown-linux-gnu" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/linux-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=aarch64" \ + "Linux ARM64 cross-toolchain (brew messense tap)" + + # Windows x86_64 via MinGW-w64. + add_target "Windows-x86-64" "build_windows_x64" "Windows-x86-64/HypergraphRewriting.dll" \ + "command -v x86_64-w64-mingw32-gcc" \ + "brew_install mingw-w64" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/windows-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=x86_64" \ + "MinGW-w64 (brew install mingw-w64)" + + # Windows ARM64 needs the Windows SDK + MSVC ARM64 import libs + a resource + # compiler, which only exist on a Windows/WSL host. Not installable on macOS. + add_target "Windows-ARM64" "build_windows_arm64" "Windows-ARM64/HypergraphRewriting.dll" \ + "[ -d '/mnt/c/Program Files (x86)/Windows Kits' ] && command -v llvm-rc" \ + "" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/windows-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=aarch64 -DWINDOWS_COMPILER=clang" \ + "Windows SDK + MSVC ARM64 libraries (WSL host only)" +} -# Windows build -if $BUILD_WINDOWS; then - echo -e "\n${GREEN}=== Building for Windows (x86_64) ===${NC}" +define_targets_linux() { + # Native Linux host build. + add_target "Linux-x86-64" "build_linux_x64" "Linux-x86-64/libHypergraphRewriting.so" \ + "command -v gcc" \ + "" \ + "cmake .. $COMMON" \ + "native gcc" + + add_target "Linux-ARM64" "build_linux_arm64" "Linux-ARM64/libHypergraphRewriting.so" \ + "command -v aarch64-linux-gnu-gcc" \ + "sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/linux-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=aarch64" \ + "gcc-aarch64-linux-gnu" + + add_target "Windows-x86-64" "build_windows_x64" "Windows-x86-64/HypergraphRewriting.dll" \ + "command -v x86_64-w64-mingw32-gcc" \ + "sudo apt-get install -y gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/windows-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=x86_64" \ + "gcc-mingw-w64-x86-64" + + add_target "Windows-ARM64" "build_windows_arm64" "Windows-ARM64/HypergraphRewriting.dll" \ + "[ -d '/mnt/c/Program Files (x86)/Windows Kits' ] && command -v llvm-rc" \ + "" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/windows-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=aarch64 -DWINDOWS_COMPILER=clang" \ + "Windows SDK + MSVC ARM64 libraries (WSL host only)" + + # macOS targets from Linux require the OSXCross SDK; left to the maintainer. + local osx="${OSXCROSS_ROOT:-$HOME/osxcross}" + add_target "MacOSX-x86-64" "build_macos_x64" "MacOSX-x86-64/libHypergraphRewriting.dylib" \ + "[ -d '$osx' ]" "" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/macos-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=x86_64" \ + "OSXCross SDK at \$OSXCROSS_ROOT" + add_target "MacOSX-ARM64" "build_macos_arm64" "MacOSX-ARM64/libHypergraphRewriting.dylib" \ + "[ -d '$osx' ]" "" \ + "cmake .. $COMMON -DCMAKE_TOOLCHAIN_FILE=$TOOLCHAINS/macos-cross.cmake -DCMAKE_SYSTEM_PROCESSOR=arm64" \ + "OSXCross SDK at \$OSXCROSS_ROOT" +} - if ! check_prerequisite "MinGW" "x86_64-w64-mingw32-gcc" "sudo apt install gcc-mingw-w64-x86-64 (Linux)"; then - echo -e "${RED}Skipping Windows build - MinGW not available${NC}" - BUILD_WINDOWS=false - else - mkdir -p build_windows - cd build_windows +case "$HOST_OS" in + Darwin) define_targets_darwin ;; + Linux) define_targets_linux ;; + *) echo -e "${RED}Unsupported host OS: $HOST_OS${NC}"; exit 1 ;; +esac - # Always configure to ensure correct build settings - cmake .. \ - -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/windows-cross.cmake \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_MATHEMATICA_PACLET=ON \ - || { echo -e "${RED}Windows CMake configuration failed${NC}"; exit 1; } +# --------------------------------------------------------------------------- +# Build loop +# --------------------------------------------------------------------------- +echo -e "${GREEN}=== Hypergraph Rewriting - multi-platform build ===${NC}" +echo -e "Host: ${BLUE}${HOST_OS} ${HOST_ARCH}${NC} Jobs: ${BUILD_JOBS} Auto-install: ${AUTO_INSTALL}\n" - make -j"$BUILD_JOBS" paclet \ - || { echo -e "${RED}Windows build failed${NC}"; exit 1; } +command -v cmake >/dev/null 2>&1 || { echo -e "${RED}cmake not found - install it first${NC}"; exit 1; } - echo -e "${GREEN}✓ Windows build complete${NC}" - cd "$PROJECT_ROOT" - fi -fi +echo -e "${GREEN}Cleaning existing library files...${NC}" +rm -f paclet/LibraryResources/*/*.so paclet/LibraryResources/*/*.dll paclet/LibraryResources/*/*.dylib +echo -# Windows ARM64 build -if $BUILD_WINDOWS; then - echo -e "\n${GREEN}=== Building for Windows (ARM64) ===${NC}" +RESULT_NAMES=(); RESULT_STATUS=() # status: BUILT | SKIPPED | FAILED +record() { RESULT_NAMES+=("$1"); RESULT_STATUS+=("$2"); } - if ! check_prerequisite "Clang" "clang" "sudo apt install clang (Linux) or brew install llvm (macOS)"; then - echo -e "${YELLOW}Skipping Windows ARM64 build - Clang not available${NC}" - else - mkdir -p build_windows_arm64 - cd build_windows_arm64 - - # Always configure to ensure correct build settings - cmake .. \ - -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/windows-cross.cmake \ - -DCMAKE_SYSTEM_PROCESSOR=aarch64 \ - -DWINDOWS_COMPILER=clang \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_MATHEMATICA_PACLET=ON \ - || { echo -e "${RED}Windows ARM64 CMake configuration failed${NC}"; exit 1; } - - make -j"$BUILD_JOBS" paclet \ - || { echo -e "${RED}Windows ARM64 build failed${NC}"; exit 1; } - - echo -e "${GREEN}✓ Windows ARM64 build complete${NC}" - cd "$PROJECT_ROOT" - fi -fi +family_wanted() { + case "$1" in + MacOSX-*) $WANT_MACOS ;; + Linux-*) $WANT_LINUX ;; + Windows-*) $WANT_WINDOWS ;; + esac +} -# macOS build (x86_64) -if $BUILD_MACOS; then - echo -e "\n${GREEN}=== Building for macOS (x86_64) ===${NC}" - - # Check for OSXCross - OSXCROSS_ROOT=${OSXCROSS_ROOT:-$HOME/osxcross} - if [ ! -d "$OSXCROSS_ROOT" ]; then - echo -e "${YELLOW}Warning: OSXCross not found at $OSXCROSS_ROOT${NC}" - echo -e "${YELLOW}Set OSXCROSS_ROOT environment variable or install OSXCross${NC}" - echo -e "${YELLOW}See: https://github.com/tpoechtrager/osxcross${NC}" - echo -e "${RED}Skipping macOS build${NC}" - BUILD_MACOS=false - else - export OSXCROSS_ROOT - export PATH="$OSXCROSS_ROOT/target/bin:$PATH" - mkdir -p build_macos - cd build_macos - - # Always configure to ensure correct build settings - cmake .. \ - -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/macos-cross.cmake \ - -DCMAKE_SYSTEM_PROCESSOR=x86_64 \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_MATHEMATICA_PACLET=ON \ - || { echo -e "${RED}macOS x86_64 CMake configuration failed${NC}"; exit 1; } - - make -j"$BUILD_JOBS" paclet \ - || { echo -e "${RED}macOS x86_64 build failed${NC}"; exit 1; } - - echo -e "${GREEN}✓ macOS x86_64 build complete${NC}" - cd "$PROJECT_ROOT" +for ((i=0; i<${#T_NAME[@]}; i++)); do + name="${T_NAME[i]}"; dir="${T_DIR[i]}"; out="${T_OUT[i]}" + probe="${T_PROBE[i]}"; install="${T_INSTALL[i]}"; config="${T_CONFIG[i]}"; hint="${T_HINT[i]}" + + if ! family_wanted "$name"; then continue; fi + + echo -e "${GREEN}=== ${name} ===${NC}" + + # Toolchain availability + if ! eval "$probe" >/dev/null 2>&1; then + if [ -n "$install" ] && $AUTO_INSTALL; then + echo -e "${YELLOW}Toolchain missing - auto-installing ($hint)...${NC}" + if eval "$install" >/dev/null 2>&1 && eval "$probe" >/dev/null 2>&1; then + echo -e "${GREEN}Toolchain installed.${NC}" + else + echo -e "${YELLOW}Auto-install did not produce a usable toolchain - skipping ${name}.${NC}\n" + record "$name" "SKIPPED"; continue + fi + else + if [ -z "$install" ]; then + echo -e "${YELLOW}Skipping ${name} - not supported on this host: ${hint}.${NC}\n" + else + echo -e "${YELLOW}Skipping ${name} - toolchain missing (${hint}); re-run without --no-install to add it.${NC}\n" + fi + record "$name" "SKIPPED"; continue + fi fi -fi -# macOS build (ARM64) -if $BUILD_MACOS; then - echo -e "\n${GREEN}=== Building for macOS (ARM64) ===${NC}" - - # Check for OSXCross (already validated above) - if [ -d "$OSXCROSS_ROOT" ]; then - export OSXCROSS_ROOT - export PATH="$OSXCROSS_ROOT/target/bin:$PATH" - mkdir -p build_macos_arm64 - cd build_macos_arm64 - - # Always configure to ensure correct build settings - cmake .. \ - -DCMAKE_TOOLCHAIN_FILE=../cmake/toolchains/macos-cross.cmake \ - -DCMAKE_SYSTEM_PROCESSOR=arm64 \ - -DCMAKE_BUILD_TYPE=Release \ - -DBUILD_MATHEMATICA_PACLET=ON \ - || { echo -e "${RED}macOS ARM64 CMake configuration failed${NC}"; exit 1; } - - make -j"$BUILD_JOBS" paclet \ - || { echo -e "${RED}macOS ARM64 build failed${NC}"; exit 1; } - - echo -e "${GREEN}✓ macOS ARM64 build complete${NC}" - cd "$PROJECT_ROOT" + # Configure + build + mkdir -p "$dir"; ( cd "$dir" \ + && eval "$config" >/dev/null 2>&1 \ + && make -j"$BUILD_JOBS" paclet >/dev/null 2>&1 ) + if [ $? -eq 0 ] && [ -f "paclet/LibraryResources/$out" ]; then + echo -e "${GREEN}✓ ${name} built${NC}\n" + record "$name" "BUILT" + else + echo -e "${RED}✗ ${name} build FAILED (see $dir for logs)${NC}\n" + record "$name" "FAILED" fi -fi +done +# --------------------------------------------------------------------------- # Summary -echo -e "\n${GREEN}=== Build Summary ===${NC}" - -# Track build status -BUILD_FAILED=false - -# Check Linux x86-64 -if [ -f paclet/LibraryResources/Linux-x86-64/libHypergraphRewriting.so ]; then - echo -e "${GREEN}✓ Linux-x86-64${NC}" -else - echo -e "${RED}✗ Linux-x86-64 - MISSING${NC}" - BUILD_FAILED=true -fi - -# Check Linux ARM64 -if [ -f paclet/LibraryResources/Linux-ARM64/libHypergraphRewriting.so ]; then - echo -e "${GREEN}✓ Linux-ARM64${NC}" -else - echo -e "${RED}✗ Linux-ARM64 - MISSING${NC}" - BUILD_FAILED=true -fi - -# Check Windows x86-64 -if [ -f paclet/LibraryResources/Windows-x86-64/HypergraphRewriting.dll ]; then - echo -e "${GREEN}✓ Windows-x86-64${NC}" -else - echo -e "${RED}✗ Windows-x86-64 - MISSING${NC}" - BUILD_FAILED=true -fi +# --------------------------------------------------------------------------- +echo -e "${GREEN}=== Build Summary ===${NC}" +any_failed=false; any_built=false +for ((i=0; i<${#RESULT_NAMES[@]}; i++)); do + case "${RESULT_STATUS[i]}" in + BUILT) echo -e "${GREEN}✓ ${RESULT_NAMES[i]}${NC}"; any_built=true ;; + SKIPPED) echo -e "${YELLOW}- ${RESULT_NAMES[i]} (skipped - not buildable on this host)${NC}" ;; + FAILED) echo -e "${RED}✗ ${RESULT_NAMES[i]} (build error)${NC}"; any_failed=true ;; + esac +done +echo -e "\nPaclet libraries in: ${BLUE}paclet/LibraryResources/${NC}" -# Check Windows ARM64 -if [ -f paclet/LibraryResources/Windows-ARM64/HypergraphRewriting.dll ]; then - echo -e "${GREEN}✓ Windows-ARM64${NC}" -else - echo -e "${RED}✗ Windows-ARM64 - MISSING${NC}" - BUILD_FAILED=true +if $any_failed; then + echo -e "\n${RED}A build that was attempted failed. Fix it and re-run.${NC}" + exit 1 fi - -# Check macOS x86-64 -if [ -f paclet/LibraryResources/MacOSX-x86-64/libHypergraphRewriting.dylib ]; then - echo -e "${GREEN}✓ MacOSX-x86-64${NC}" -else - echo -e "${RED}✗ MacOSX-x86-64 - MISSING${NC}" - BUILD_FAILED=true +if ! $any_built; then + echo -e "\n${RED}Nothing was built.${NC}" + exit 1 fi -# Check macOS ARM64 -if [ -f paclet/LibraryResources/MacOSX-ARM64/libHypergraphRewriting.dylib ]; then - echo -e "${GREEN}✓ MacOSX-ARM64${NC}" -else - echo -e "${RED}✗ MacOSX-ARM64 - MISSING${NC}" - BUILD_FAILED=true +# --------------------------------------------------------------------------- +# Assemble the paclet archive (everything that built is packed in) +# --------------------------------------------------------------------------- +if $WANT_ARCHIVE; then + echo -e "\n${GREEN}=== Assembling paclet archive ===${NC}" + native_dir="build_linux" + [ "$HOST_OS" = "Darwin" ] && native_dir="build_macos_arm64" + if [ -d "$native_dir" ]; then + ( cd "$native_dir" && make create_paclet_archive ) \ + && echo -e "${GREEN}✓ Paclet archive in paclet_archive/${NC}" \ + || echo -e "${YELLOW}Archive assembly failed (libraries are still in paclet/LibraryResources/).${NC}" + else + echo -e "${YELLOW}No native build dir to drive archive creation; run 'make create_paclet_archive' in any build dir.${NC}" + fi fi -echo -e "\n${GREEN}Paclet libraries located in: paclet/LibraryResources/${NC}" - -if $BUILD_FAILED; then - echo -e "\n${RED}ERROR: Some platform libraries are missing!${NC}" - echo -e "${RED}Build incomplete - do not create paclet archive${NC}" - exit 1 -else - echo -e "\n${GREEN}✓ All platform libraries built successfully${NC}" - echo -e "\nTo create paclet archive: cd build_ && make create_paclet_archive" -fi +echo -e "\n${GREEN}✓ Done.${NC}" diff --git a/cmake/FindMathematica.cmake b/cmake/FindMathematica.cmake index b99f5fd..dfadacc 100644 --- a/cmake/FindMathematica.cmake +++ b/cmake/FindMathematica.cmake @@ -68,9 +68,13 @@ else() elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") # macOS + # Note: Wolfram 14+ ships as "Wolfram.app"; older releases as + # "Mathematica.app", and the free kernel as "Wolfram Engine.app". list(APPEND SEARCH_PATHS + "/Applications/Wolfram.app/Contents" "/Applications/Mathematica.app/Contents" "/Applications/Wolfram Engine.app/Contents" + "$ENV{HOME}/Applications/Wolfram.app/Contents" "$ENV{HOME}/Applications/Mathematica.app/Contents" "$ENV{HOME}/Applications/Wolfram Engine.app/Contents" ) diff --git a/paclet_source/CMakeLists.txt b/paclet_source/CMakeLists.txt index 42c2850..e20bd40 100644 --- a/paclet_source/CMakeLists.txt +++ b/paclet_source/CMakeLists.txt @@ -85,7 +85,14 @@ if(Mathematica_INSTALL_DIR) message(STATUS "WSTP SDK not found for Linux - progress reporting disabled") endif() elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + # Honour CMAKE_OSX_ARCHITECTURES when present (native cross-arch builds, + # e.g. building an x86_64 dylib on an Apple-silicon host). Fall back to + # the host/processor when it is not set. + if(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64") + set(WSTP_PLATFORM_DIR "MacOSX-x86-64") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "arm64") + set(WSTP_PLATFORM_DIR "MacOSX-ARM64") + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") set(WSTP_PLATFORM_DIR "MacOSX-ARM64") else() set(WSTP_PLATFORM_DIR "MacOSX-x86-64") @@ -94,6 +101,10 @@ if(Mathematica_INSTALL_DIR) if(EXISTS "${WSTP_INCLUDE_DIR}/wstp.h") target_include_directories(HypergraphRewriting PRIVATE ${WSTP_INCLUDE_DIR}) target_compile_definitions(HypergraphRewriting PRIVATE HAVE_WSTP=1) + # Link against the WSTP static library for macOS. + # libWSTPi4.a additionally requires -framework Foundation, + # which is already added in the Darwin linker-settings block below. + target_link_libraries(HypergraphRewriting "${WSTP_INCLUDE_DIR}/libWSTPi4.a") message(STATUS "WSTP SDK found at ${WSTP_INCLUDE_DIR}") else() message(STATUS "WSTP SDK not found for macOS - progress reporting disabled") @@ -200,8 +211,14 @@ endif() # Determine platform directory based on CMAKE_SYSTEM_NAME (set by toolchain) if(NOT DEFINED PLATFORM_DIR) if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - # macOS - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + # macOS - honour CMAKE_OSX_ARCHITECTURES for native cross-arch builds + # (e.g. an x86_64 dylib produced on Apple silicon) before falling back + # to the host processor. + if(CMAKE_OSX_ARCHITECTURES MATCHES "x86_64") + set(PLATFORM_DIR "MacOSX-x86-64") + elseif(CMAKE_OSX_ARCHITECTURES MATCHES "arm64") + set(PLATFORM_DIR "MacOSX-ARM64") + elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") set(PLATFORM_DIR "MacOSX-ARM64") else() set(PLATFORM_DIR "MacOSX-x86-64")