diff --git a/.github/workflows/build-script-test.yml b/.github/workflows/build-script-test.yml new file mode 100644 index 0000000000..905c5a9699 --- /dev/null +++ b/.github/workflows/build-script-test.yml @@ -0,0 +1,271 @@ +name: Build Script Test + +permissions: + contents: read + +on: + push: + branches: [ main,charishma_build_script ] + paths: + - 'build_liboqs.sh' + - 'requirements.txt' + - 'CMakeLists.txt' + - 'src/**/CMakeLists.txt' + - '.github/workflows/build-script-test.yml' + pull_request: + paths: + - 'build_liboqs.sh' + - 'requirements.txt' + - 'CMakeLists.txt' + - 'src/**/CMakeLists.txt' + - '.github/workflows/build-script-test.yml' + workflow_dispatch: + +jobs: + test-build-script: + name: Test build script on ${{ matrix.os }} - ${{ matrix.test-name }} + strategy: + fail-fast: false + matrix: + include: + # Essential Ubuntu tests - covers core functionality + - os: ubuntu-latest + test-name: default-build + script-args: "" + description: "Default configuration (most common use case)" + + - os: ubuntu-latest + test-name: minimal-build + script-args: '--minimal-build "KEM_ml_kem_768;SIG_ml_dsa_44"' + description: "Minimal build with specific algorithms" + test-kem-alg: "ML-KEM-768" + test-sig-alg: "ML-DSA-44" + + - os: ubuntu-latest + test-name: shared-no-openssl + script-args: "--shared --no-openssl" + description: "Shared library without OpenSSL" + + # Cross-platform verification - macOS tests + - os: macos-latest + test-name: default-build + script-args: "" + description: "Verify script works on macOS" + + - os: macos-latest + test-name: minimal-build-multi-alg + script-args: '--minimal-build "KEM_ml_kem_512;KEM_ml_kem_768;KEM_ml_kem_1024;SIG_ml_dsa_44;SIG_ml_dsa_65;SIG_falcon_512"' + description: "Minimal build with multiple algorithms on macOS" + test-kem-alg: "ML-KEM-768" + test-sig-alg: "ML-DSA-44" + + # NixOS test - verify Nix flake integration + - os: ubuntu-latest + test-name: nixos-flake + script-args: "" + description: "Test NixOS/Nix flake integration" + use-nix: true + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2 + + - name: Install Nix (for NixOS tests) + if: matrix.use-nix == true + uses: nixbuild/nix-quick-install-action@v28 + with: + nix_conf: | + experimental-features = nix-command flakes + + - name: Make build script executable + run: chmod +x build_liboqs.sh + + - name: Run build script - ${{ matrix.test-name }} (NixOS) + if: matrix.use-nix == true + env: + SKIP_STALENESS_CHECK: 1 + run: | + # Simulate NixOS by unsetting IN_NIX_SHELL to test auto-detection + unset IN_NIX_SHELL + # The script should automatically re-execute itself with 'nix develop -c' + ./build_liboqs.sh ${{ matrix.script-args }} + + - name: Run build script - ${{ matrix.test-name }} (non-NixOS) + if: matrix.use-nix != true + env: + SKIP_STALENESS_CHECK: 1 + run: ./build_liboqs.sh ${{ matrix.script-args }} + + - name: Verify build artifacts exist + run: | + if [ ! -d "build" ]; then + echo "Error: build directory not created" + exit 1 + fi + if [ ! -f "build/lib/liboqs.a" ] && [ ! -f "build/lib/liboqs.dylib" ] && [ ! -f "build/lib/liboqs.so" ]; then + echo "Error: liboqs library not found" + exit 1 + fi + echo "✓ Build artifacts verified" + + - name: Run basic library test + run: | + cd build + # Check if test executables exist and run a quick test + if [ -f "tests/test_kem" ]; then + echo "Running KEM test..." + # Use specified algorithm if provided, otherwise auto-detect + if [ -n "${{ matrix.test-kem-alg }}" ]; then + KEM_ALG="${{ matrix.test-kem-alg }}" + echo "Testing with specified algorithm: $KEM_ALG" + else + # Get list of available algorithms and pick the first one + KEM_ALG=$(./tests/test_kem 2>&1 | grep "algname:" | sed 's/.*algname: //' | cut -d',' -f1 | tr -d ' ') + echo "Testing with first available algorithm: $KEM_ALG" + fi + + if [ -n "$KEM_ALG" ]; then + ./tests/test_kem "$KEM_ALG" + echo "✓ KEM test passed with $KEM_ALG" + else + echo "⚠ Could not determine available KEM algorithms" + fi + else + echo "KEM test executable not found (may be disabled in minimal build)" + fi + + if [ -f "tests/test_sig" ]; then + echo "Running SIG test..." + # Use specified algorithm if provided, otherwise auto-detect + if [ -n "${{ matrix.test-sig-alg }}" ]; then + SIG_ALG="${{ matrix.test-sig-alg }}" + echo "Testing with specified algorithm: $SIG_ALG" + else + # Get list of available algorithms and pick the first one + SIG_ALG=$(./tests/test_sig 2>&1 | grep "algname:" | sed 's/.*algname: //' | cut -d',' -f1 | tr -d ' ') + echo "Testing with first available algorithm: $SIG_ALG" + fi + + if [ -n "$SIG_ALG" ]; then + ./tests/test_sig "$SIG_ALG" + echo "✓ SIG test passed with $SIG_ALG" + else + echo "⚠ Could not determine available SIG algorithms" + fi + else + echo "SIG test executable not found (may be disabled in minimal build)" + fi + + test-script-options-coverage: + name: Verify script covers all CMake options + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2 + + - name: Extract CMake options from CONFIGURE.md + run: | + # Extract OQS_ prefixed options from CONFIGURE.md + grep -oE 'OQS_[A-Z_]+' CONFIGURE.md | sort -u > cmake_options.txt || true + echo "CMake options found in CONFIGURE.md:" + cat cmake_options.txt + + - name: Extract options from build script + run: | + # Extract OQS_ prefixed options from build_liboqs.sh + grep -oE 'OQS_[A-Z_]+' build_liboqs.sh | sort -u > script_options.txt || true + echo "Options found in build_liboqs.sh:" + cat script_options.txt + + - name: Compare coverage + shell: bash + run: | + echo "Checking if build script covers major CMake options..." + # Check for key options that should be in the script (using word boundaries to avoid partial matches) + echo "Checking for OQS_USE_OPENSSL..." + if grep -qE '\bOQS_USE_OPENSSL\b' build_liboqs.sh; then + echo "✓ Found: OQS_USE_OPENSSL" + else + echo "❌ Missing: OQS_USE_OPENSSL" + fi + + echo "Checking for OQS_DIST_BUILD..." + if grep -qE '\bOQS_DIST_BUILD\b' build_liboqs.sh; then + echo "✓ Found: OQS_DIST_BUILD" + else + echo "❌ Missing: OQS_DIST_BUILD" + fi + + echo "Checking for OQS_MINIMAL_BUILD..." + if grep -qE '\bOQS_MINIMAL_BUILD\b' build_liboqs.sh; then + echo "✓ Found: OQS_MINIMAL_BUILD" + else + echo "❌ Missing: OQS_MINIMAL_BUILD" + fi + + echo "Checking for OQS_BUILD_ONLY_LIB..." + if grep -qE '\bOQS_BUILD_ONLY_LIB\b' build_liboqs.sh; then + echo "✓ Found: OQS_BUILD_ONLY_LIB" + else + echo "❌ Missing: OQS_BUILD_ONLY_LIB" + fi + + echo "Checking for OQS_ALGS_ENABLED..." + if grep -qE '\bOQS_ALGS_ENABLED\b' build_liboqs.sh; then + echo "✓ Found: OQS_ALGS_ENABLED" + else + echo "❌ Missing: OQS_ALGS_ENABLED" + fi + + echo "✓ Coverage check complete" + + test-script-help: + name: Verify script help and usage + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2 + + - name: Make build script executable + run: chmod +x build_liboqs.sh + + - name: Test help flag + run: | + ./build_liboqs.sh --help > help_output.txt 2>&1 + if [ ! -s help_output.txt ]; then + echo "Error: Help output is empty" + exit 1 + fi + echo "✓ Help flag works" + cat help_output.txt + + - name: Test invalid option handling + run: | + if ./build_liboqs.sh --invalid-option 2>&1 | grep -q "Unknown option"; then + echo "✓ Invalid option handling works" + else + echo "Error: Script should reject invalid options" + exit 1 + fi + + test-script-syntax: + name: Verify script syntax and shellcheck + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # pin@v4.2.2 + + - name: Install shellcheck + run: sudo apt-get update && sudo apt-get install -y shellcheck + + - name: Check bash syntax + run: bash -n build_liboqs.sh + + - name: Run shellcheck + run: | + shellcheck build_liboqs.sh || echo "Shellcheck warnings found (non-blocking)" diff --git a/README.md b/README.md index 6564c9190f..ee6091b62f 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ liboqs is an open source C library for quantum-safe cryptographic algorithms. - [Support limitations](#support-limitations) - [Quickstart](#quickstart) - [Linux and Mac](#linux-and-mac) + - [Using the build script](#using-the-build-script) - [Windows](#windows) - [Cross compilation](#cross-compilation) - [Documentation](#documentation) @@ -173,6 +174,69 @@ The following instructions assume we are in `build`. 5. `ninja uninstall` can be run to remove all installation files. +### Using the build script + +`build_liboqs.sh` is a convenience wrapper around the standard CMake + Ninja build that handles OS detection, dependency installation, and exposes the most common [CMake options](CONFIGURE.md) as named flags. + +**Basic usage** + +```sh +# Default build (installs deps, static library, all algorithms, Release mode) +./build_liboqs.sh + +# Shared library, Debug build +./build_liboqs.sh --shared --build-type Debug + +# Minimal build — only the algorithms you need (quote the list to protect `;`) +./build_liboqs.sh --minimal-build "KEM_ml_kem_768;SIG_ml_dsa_44" + +# Skip OpenSSL dependency +./build_liboqs.sh --no-openssl + +# Show all available flags +./build_liboqs.sh --help +``` + +**Script internals, step by step** + +1. **Detects the OS** (macOS, Ubuntu/Debian, NixOS) and installs missing build dependencies automatically — Homebrew on macOS, `apt` on Debian/Ubuntu, `nix develop` on NixOS. +2. **Checks for build-directory conflicts** — if an existing `build/` was created with a different CMake generator it is removed before proceeding. +3. **Runs `cmake -GNinja`** inside `./build/` with any flags you provided, plus these defaults when none are given: + - `CMAKE_BUILD_TYPE=Release` + - `OQS_DIST_BUILD=ON` (portable across CPUs) + - `OQS_USE_OPENSSL=ON` + - `OQS_ALGS_ENABLED=All` +4. **Runs `ninja`** to compile the library and (unless `--build-only-lib`) the test harnesses under `build/tests/`. +5. **Prints next-step hints** — how to run `ninja install`, `ninja run_tests`, and install Python test dependencies. + +**Dependency-check-only mode** + +Pass `--build-only` to verify all required tools are present without installing or building anything: + +```sh +./build_liboqs.sh --build-only +``` + +**Key flags reference** + +| Flag | CMake equivalent | Description | +|------|-----------------|-------------| +| `--shared` | `-DBUILD_SHARED_LIBS=ON` | Build a shared library instead of static | +| `--build-type TYPE` | `-DCMAKE_BUILD_TYPE=TYPE` | `Debug`, `Release`, `RelWithDebInfo`, `MinSizeRel` | +| `--minimal-build "A;B"` | `-DOQS_MINIMAL_BUILD="A;B"` | Build only the listed algorithms | +| `--algs-enabled SET` | `-DOQS_ALGS_ENABLED=SET` | `STD`, `NIST_R4`, `NIST_SIG_ONRAMP`, `All` | +| `--no-openssl` | `-DOQS_USE_OPENSSL=OFF` | Disable OpenSSL dependency | +| `--build-only-lib` | `-DOQS_BUILD_ONLY_LIB=ON` | Skip tests and docs | +| `--no-dist-build` | `-DOQS_DIST_BUILD=OFF` | Optimise for the current machine only | +| `--install-prefix PATH` | `-DCMAKE_INSTALL_PREFIX=PATH` | Where `ninja install` puts files | +| `-D KEY=VALUE` | `-DKEY=VALUE` | Pass any CMake option directly | + +Any CMake option not listed above can still be passed with `-D`, e.g.: + +```sh +./build_liboqs.sh -DOQS_SPEED_USE_ARM_PMU=ON +``` + ### Windows Binaries can be generated using Visual Studio 2019 with the [CMake Tools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools) extension installed. The same options as explained above for Linux/macOS can be used and build artifacts are generated in the specified `build` folders. diff --git a/build_liboqs.sh b/build_liboqs.sh new file mode 100755 index 0000000000..a56be100d0 --- /dev/null +++ b/build_liboqs.sh @@ -0,0 +1,656 @@ +#!/bin/bash + +# build_liboqs.sh - Build script for liboqs with OS detection and dependency installation +# Supports runtime CMake configuration options from CONFIGURE.md + +set -e # Exit on error + +# Color codes for output +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to display usage information +usage() { + cat << EOF +Usage: $0 [OPTIONS] + +Build liboqs with optional CMake configuration options. + +DEFAULT SETTINGS (when no options specified): + - Build type: Release + - Library type: Static (.a) + - Algorithm set: All (OQS_ALGS_ENABLED=All) + - Distribution build: ON (OQS_DIST_BUILD=ON) + - OpenSSL: ON (OQS_USE_OPENSSL=ON) + - Optimization target: auto (OQS_OPT_TARGET=auto) + - Tests and docs: Built (OQS_BUILD_ONLY_LIB=OFF) + - Dependencies: Install automatically and build + +OPTIONS: + -h, --help Show this help message + + Dependency Management: + --build-only Only build (check dependencies and exit with error if missing) + + Build Configuration: + --shared Build shared library (BUILD_SHARED_LIBS=ON) + --build-type TYPE Set build type: Debug, Release, MinSizeRel, RelWithDebInfo (default: Release) + --install-prefix PATH Set installation prefix (CMAKE_INSTALL_PREFIX) + + Algorithm Selection: + --algs-enabled SET Algorithm set: STD, NIST_R4, NIST_SIG_ONRAMP, All (default: All) + --minimal-build "ALG1;ALG2;..." Build ONLY specified algorithms (disables all others) + Supports multiple semicolon-separated algorithms + Works for KEM, SIG, and SIG_STFL families + (e.g., "KEM_ml_kem_768;SIG_ml_dsa_44;SIG_falcon_512") + IMPORTANT: Must be quoted to prevent shell from treating ; as command separator + --enable-kem-ALG Enable specific KEM algorithm (additive with defaults) + --enable-sig-ALG Enable specific signature algorithm (additive with defaults) + --enable-sig-stfl-ALG Enable specific stateful signature algorithm (additive with defaults) + + Build Options: + --build-only-lib Build only library, exclude tests and docs (OQS_BUILD_ONLY_LIB=ON) + --dist-build Build for distribution (OQS_DIST_BUILD=ON, default) + --no-dist-build Build for single machine (OQS_DIST_BUILD=OFF) + --opt-target TARGET Optimization target: auto, generic, or specific CPU (default: auto) + + OpenSSL Options: + --use-openssl Use OpenSSL (OQS_USE_OPENSSL=ON, default) + --no-openssl Don't use OpenSSL (OQS_USE_OPENSSL=OFF) + --openssl-root PATH OpenSSL root directory (OPENSSL_ROOT_DIR) + --dlopen-openssl Dynamically load OpenSSL (OQS_DLOPEN_OPENSSL=ON) + + GPU Acceleration: + --use-cupqc Use NVIDIA cuPQC library (OQS_USE_CUPQC=ON) + --use-icicle Use ICICLE GPU acceleration (OQS_USE_ICICLE=ON) + + CPU Features (for non-dist builds): + --use-adx Use ADX instructions (OQS_USE_ADX_INSTRUCTIONS=ON) + --use-aes Use AES instructions (OQS_USE_AES_INSTRUCTIONS=ON) + --use-avx Use AVX instructions (OQS_USE_AVX_INSTRUCTIONS=ON) + --use-avx2 Use AVX2 instructions (OQS_USE_AVX2_INSTRUCTIONS=ON) + --use-avx512 Use AVX512 instructions (OQS_USE_AVX512_INSTRUCTIONS=ON) + + Advanced Options: + --embedded-build Build for embedded systems (OQS_EMBEDDED_BUILD=ON) + --memopt-build Use memory-optimized implementations (OQS_MEMOPT_BUILD=ON) + --libjade-build Use Libjade implementations (OQS_LIBJADE_BUILD=ON) + --permit-unsupported-arch Permit compilation on unsupported architecture (OQS_PERMIT_UNSUPPORTED_ARCHITECTURE=ON) + --strict-warnings Enable strict compiler warnings (OQS_STRICT_WARNINGS=ON) + --enable-constant-time-test Enable constant-time testing (OQS_ENABLE_TEST_CONSTANT_TIME=ON) + --use-coverage Enable code coverage (USE_COVERAGE=ON) + --use-sanitizer TYPE Enable sanitizer: Address, Memory, Undefined, Thread, Leak + + Stateful Signatures: + --enable-xmss Enable XMSS stateful signatures (OQS_ENABLE_SIG_STFL_XMSS=ON) + --enable-lms Enable LMS stateful signatures (OQS_ENABLE_SIG_STFL_LMS=ON) + + Note: Stateful key/signature generation is HAZARDOUS and disabled by default. + To enable (NOT RECOMMENDED), use: -DOQS_HAZARDOUS_EXPERIMENTAL_ENABLE_SIG_STFL_KEY_SIG_GEN=ON + See CONFIGURE.md for security implications before enabling. + + Custom CMake Options: + -D KEY=VALUE Pass custom CMake option directly + +EXAMPLES: + # Basic build with defaults + $0 + + # Build shared library with debug symbols + $0 --shared --build-type Debug + + # Minimal build with only ML-KEM-768 and ML-DSA-44 + $0 --minimal-build "KEM_ml_kem_768;SIG_ml_dsa_44" + + # Build for distribution with OpenSSL + $0 --dist-build --use-openssl + + # Build with GPU acceleration + $0 --use-icicle + + # Build with custom options + $0 --build-type Release --strict-warnings -DOQS_USE_AVX2_INSTRUCTIONS=ON + +For more details, see CONFIGURE.md + +EOF + exit 0 +} + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Dependency management mode +DEPS_MODE="install" # Default: install dependencies and build + +# Function to check if a command exists +command_exists() { + command -v "$1" &> /dev/null +} + +# Function to check dependencies +check_dependencies() { + local missing_deps=() + local os_type="" + + # Detect OS + if [[ "$OSTYPE" == "darwin"* ]]; then + os_type="macos" + + # Check for Homebrew + if ! command_exists brew; then + missing_deps+=("homebrew (https://brew.sh)") + fi + + # Check for required tools + local required_tools=("cmake" "ninja" "wget" "doxygen" "graphviz" "astyle" "python3") + for tool in "${required_tools[@]}"; do + if ! command_exists "$tool"; then + missing_deps+=("$tool") + fi + done + + # Check for OpenSSL 3 + if ! brew list openssl@3 &> /dev/null && ! [ -d "/usr/local/opt/openssl@3" ] && ! [ -d "/opt/homebrew/opt/openssl@3" ]; then + missing_deps+=("openssl@3") + fi + + elif [[ -f /etc/os-release ]]; then + . /etc/os-release + + if [[ "$ID" == "ubuntu" ]] || [[ "$ID" == "debian" ]] || [[ "$ID_LIKE" == *"debian"* ]]; then + os_type="debian" + + # Check for required packages + local required_packages=("cmake" "gcc" "ninja-build" "libssl-dev" "python3-pytest" "python3-pytest-xdist" "unzip" "xsltproc" "doxygen" "graphviz" "astyle" "python3-yaml" "valgrind") + for pkg in "${required_packages[@]}"; do + if ! dpkg -l | grep -q "^ii $pkg"; then + missing_deps+=("$pkg") + fi + done + + elif [[ "$ID" == "nixos" ]]; then + os_type="nixos" + # For NixOS, check if in nix-shell + if [ -z "$IN_NIX_SHELL" ]; then + echo -e "${YELLOW}Note: Not in Nix development environment${NC}" + echo "Run: nix develop" + return 1 + fi + else + echo -e "${RED}Error: Unsupported Linux distribution: $NAME${NC}" + return 1 + fi + else + echo -e "${RED}Error: Unable to detect operating system${NC}" + return 1 + fi + + # Report missing dependencies + if [ ${#missing_deps[@]} -gt 0 ]; then + echo -e "${RED}Error: Missing required dependencies:${NC}" + for dep in "${missing_deps[@]}"; do + echo -e "${RED} - $dep${NC}" + done + echo "" + + if [[ "$os_type" == "macos" ]]; then + echo "Install with:" + echo " brew install cmake ninja openssl@3 wget doxygen graphviz astyle python3" + elif [[ "$os_type" == "debian" ]]; then + echo "Install with:" + echo " sudo apt update" + echo " sudo apt install -y astyle cmake gcc ninja-build libssl-dev unzip xsltproc doxygen graphviz valgrind" + elif [[ "$os_type" == "nixos" ]]; then + echo "Enter Nix development environment with:" + echo " nix develop" + fi + echo "" + echo "For Python testing dependencies, install separately:" + echo " pip3 install -r requirements.txt" + return 1 + fi + + echo -e "${GREEN}✓ All dependencies are installed${NC}" + return 0 +} + +# Function to install dependencies +install_dependencies() { + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + echo -e "${GREEN}Detected OS: macOS${NC}" + echo "" + + # Check if Homebrew is installed + if ! command_exists brew; then + echo -e "${RED}Error: Homebrew is not installed!${NC}" + echo "Please install Homebrew first: https://brew.sh" + exit 1 + fi + + echo "Installing dependencies..." + brew install cmake ninja openssl@3 wget doxygen graphviz astyle python3 + + elif [[ -f /etc/os-release ]]; then + # Source the os-release file + . /etc/os-release + + if [[ "$ID" == "ubuntu" ]] || [[ "$ID" == "debian" ]] || [[ "$ID_LIKE" == *"debian"* ]]; then + # Ubuntu/Debian - but check if Nix is available first + if command_exists nix && [ -f "${SCRIPT_DIR}/flake.nix" ] && [ -z "$IN_NIX_SHELL" ]; then + # Nix is available and flake.nix exists - use Nix environment + echo -e "${GREEN}Detected OS: $NAME (with Nix available)${NC}" + echo -e "${YELLOW}Using Nix development environment for reproducible builds...${NC}" + echo "" + + # Re-execute this script inside the Nix development environment + exec nix develop "${SCRIPT_DIR}" -c "$0" "$@" + fi + + # Standard Ubuntu/Debian path + echo -e "${GREEN}Detected OS: $NAME${NC}" + echo "" + + echo "Installing dependencies..." + echo -e "${YELLOW}Note: This will use 'sudo' and may prompt for your password.${NC}" + sudo apt update + sudo apt install -y astyle cmake gcc ninja-build libssl-dev unzip xsltproc doxygen graphviz valgrind + + elif [[ "$ID" == "nixos" ]]; then + # NixOS + echo -e "${GREEN}Detected OS: NixOS${NC}" + echo "" + + # Check if already in a Nix development environment + if [ -n "$IN_NIX_SHELL" ]; then + echo "✓ Already in Nix development environment" + else + # Check if flake.nix exists + if [ ! -f "${SCRIPT_DIR}/flake.nix" ]; then + echo -e "${RED}Error: flake.nix not found in ${SCRIPT_DIR}${NC}" + echo "Cannot automatically enter Nix development environment." + exit 1 + fi + + echo -e "${YELLOW}Not in Nix development environment. Re-executing with 'nix develop'...${NC}" + echo "" + + # Re-execute this script inside the Nix development environment + # Pass all original arguments to the re-executed script + exec nix develop "${SCRIPT_DIR}" -c "$0" "$@" + fi + + else + echo -e "${YELLOW}Warning: Unsupported Linux distribution: $NAME${NC}" + echo "Please install dependencies manually." + exit 1 + fi + + else + echo -e "${RED}Error: Unable to detect operating system${NC}" + echo "Supported OS: macOS, Ubuntu, Debian, NixOS" + exit 1 + fi + + echo -e "${GREEN}Dependencies installed successfully!${NC}" +} + +# Function to check if script is outdated by validating CMake options +check_script_staleness() { + local SCRIPT_FILE="${BASH_SOURCE[0]}" + local CMAKE_FILE="${SCRIPT_DIR}/CMakeLists.txt" + local ALG_SUPPORT_FILE="${SCRIPT_DIR}/.CMake/alg_support.cmake" + local WARNED=0 + local MISSING_OPTIONS=() + + # Skip check if CMake files don't exist + if [ ! -f "$CMAKE_FILE" ] && [ ! -f "$ALG_SUPPORT_FILE" ]; then + return 0 + fi + + # Extract all option() declarations from CMake files + local CMAKE_OPTIONS=() + + # Parse main CMakeLists.txt + # Match both option() and cmake_dependent_option(), with or without leading whitespace + if [ -f "$CMAKE_FILE" ]; then + while IFS= read -r line; do + [ -n "$line" ] && CMAKE_OPTIONS+=("$line") + done < <(grep -E '^\s*(option|cmake_dependent_option)\((OQS_|USE_|BUILD_)' "$CMAKE_FILE" 2>/dev/null | sed -E 's/^\s*(option|cmake_dependent_option)\(//;s/[" ].*//' || true) + fi + + # Parse .CMake/alg_support.cmake for algorithm options + # Match both option() and cmake_dependent_option(), with or without leading whitespace + if [ -f "$ALG_SUPPORT_FILE" ]; then + while IFS= read -r line; do + [ -n "$line" ] && CMAKE_OPTIONS+=("$line") + done < <(grep -E '^\s*(option|cmake_dependent_option)\(OQS_' "$ALG_SUPPORT_FILE" 2>/dev/null | sed -E 's/^\s*(option|cmake_dependent_option)\(//;s/[" ].*//' || true) + fi + + # Remove duplicates (handle empty array safely) + if [ ${#CMAKE_OPTIONS[@]} -gt 0 ]; then + CMAKE_OPTIONS=($(printf '%s\n' "${CMAKE_OPTIONS[@]}" | sort -u || true)) + fi + + # Check each CMake option against the script + for opt in "${CMAKE_OPTIONS[@]}"; do + # Skip empty lines + [ -z "$opt" ] && continue + + # Skip algorithm-specific options that are covered by generic patterns + # (e.g., OQS_ENABLE_KEM_KYBER is covered by --enable-kem-* pattern) + if [[ "$opt" =~ ^OQS_ENABLE_KEM_ ]] || [[ "$opt" =~ ^OQS_ENABLE_SIG_ ]] || [[ "$opt" =~ ^OQS_ENABLE_LIBJADE_ ]]; then + continue + fi + + # Convert option name to script format (e.g., OQS_USE_OPENSSL -> --use-openssl or -DOQS_USE_OPENSSL) + # Check if option is referenced in the script (either as flag or -D option) + if ! grep -q "$opt" "$SCRIPT_FILE" 2>/dev/null; then + MISSING_OPTIONS+=("$opt") + fi + done + + # Report missing options + if [ ${#MISSING_OPTIONS[@]} -gt 0 ]; then + echo -e "${YELLOW}⚠️ Warning: Found CMake options not exposed in this build script:${NC}" + + # Group and display missing options (limit to first 10 to avoid clutter) + local count=0 + for opt in "${MISSING_OPTIONS[@]}"; do + if [ $count -lt 10 ]; then + echo -e "${YELLOW} - $opt${NC}" + count=$((count+1)) + fi + done + + if [ ${#MISSING_OPTIONS[@]} -gt 10 ]; then + echo -e "${YELLOW} ... and $((${#MISSING_OPTIONS[@]} - 10)) more${NC}" + fi + + echo -e "${YELLOW} You can use these options with: -D