11#! /usr/bin/env bash
2- # filepath: /workspaces/pyprophet/scripts/build/build_linux.sh
32# Build script for PyProphet Linux executable using PyInstaller
43
54set -euo pipefail
@@ -17,50 +16,30 @@ echo "Using Python version: $PYTHON_VERSION"
1716if [[ $( echo " $PYTHON_VERSION < 3.11" | bc -l) -eq 1 ]]; then
1817 echo " ERROR: Python 3.11+ is required for building."
1918 echo " Your Python version: $PYTHON_VERSION "
20- echo " "
21- echo " Please install Python 3.11 or later:"
22- echo " sudo apt update"
23- echo " sudo apt install python3.11 python3.11-venv python3.11-dev"
24- echo " "
25- echo " Then run the build with:"
26- echo " PYTHON=python3.11 bash scripts/build/build_linux.sh"
2719 exit 1
2820fi
2921
30- # Install system tools for stripping
22+ # Install system tools
3123echo " Installing build tools..."
3224sudo apt-get update -qq
3325sudo apt-get install -y binutils
3426
3527# Save the original directory
3628ORIGINAL_DIR=" $( pwd) "
3729
38- # Clean ALL build artifacts
30+ # Clean build artifacts
3931echo " Cleaning build artifacts..."
4032rm -rf build dist pyprophet.spec * .egg-info .eggs
4133find . -type d -name __pycache__ -exec rm -rf {} + 2> /dev/null || true
4234find . -type f -name " *.pyc" -delete 2> /dev/null || true
4335find . -type f -name " *.pyo" -delete 2> /dev/null || true
4436
45- # Create a clean virtual environment
46- echo " Creating clean virtual environment..."
47- VENV_DIR=$( mktemp -d) /build_venv
48- $PYTHON -m venv " $VENV_DIR "
49- source " $VENV_DIR /bin/activate"
50-
51- # Upgrade pip and install build tools
52- pip install --upgrade pip setuptools wheel build
53-
54- # Build wheel
55- echo " Building pyprophet wheel..."
56- python -m build --wheel --outdir /tmp/pyprophet_wheels
57-
58- # Install dependencies
59- echo " Installing dependencies..."
60- pip install cython numpy pyinstaller
37+ # Install/upgrade build dependencies
38+ $PYTHON -m pip install --upgrade pip setuptools wheel cython numpy pyinstaller
6139
6240# Parse and install runtime dependencies
63- python << 'PYEOF '
41+ echo " Installing runtime dependencies..."
42+ $PYTHON << 'PYEOF '
6443import tomllib
6544import subprocess
6645import sys
@@ -74,106 +53,96 @@ for dep in config['project']['dependencies']:
7453 try:
7554 print(f"Installing: {dep}")
7655 subprocess.check_call([sys.executable, '-m', 'pip', 'install', '--no-cache-dir', dep])
77- except subprocess.CalledProcessError:
78- pass
56+ except subprocess.CalledProcessError as e :
57+ print(f"Warning: Failed to install {dep}: {e}")
7958PYEOF
8059
81- # Install pyprophet from wheel (NOT editable)
82- echo " Installing pyprophet from wheel..."
83- pip install --force-reinstall --no-deps /tmp/pyprophet_wheels/pyprophet-* .whl
84-
85- # Verify installation
86- echo " Verifying installation..."
87- python -c " import pyprophet; print(f'PyProphet: {pyprophet.__file__}')"
88- python -c " import numpy; print(f'NumPy: {numpy.__file__}')"
89- python -c " import pandas; print('✓ All imports successful')"
60+ # Install the package in editable mode
61+ echo " Installing pyprophet in editable mode..."
62+ $PYTHON -m pip install -e .
9063
91- # Get site-packages
92- SITE_PACKAGES= $( python -c " import pyprophet, os; print(os.path.dirname(pyprophet.__file__)) " )
93- echo " Site-packages: ${SITE_PACKAGES} "
64+ # Build C extensions in-place
65+ echo " Building C extensions... "
66+ $PYTHON setup.py build_ext --inplace
9467
95- # Collect binaries
68+ # Collect compiled extension binaries
9669ADD_BINARY_ARGS=()
97- for so in " ${SITE_PACKAGES} " / pyprophet/scoring/_optimized* .so; do
70+ for so in pyprophet/scoring/_optimized * .so pyprophet/scoring/_optimized* .cpython- * .so; do
9871 if [ -f " $so " ]; then
9972 ADD_BINARY_ARGS+=(--add-binary " $so :pyprophet/scoring" )
100- echo " Found binary: $so "
73+ echo " Adding binary: $so "
10174 fi
10275done
10376
104- # Create build directory
105- mkdir -p " ${ORIGINAL_DIR} /dist"
106- BUILD_DIR=$( mktemp -d)
107- cd " ${BUILD_DIR} "
77+ # Locate xgboost native library
78+ SO_PATH=$( $PYTHON - << 'PY '
79+ import importlib, os
80+ try:
81+ m = importlib.import_module("xgboost")
82+ p = os.path.join(os.path.dirname(m.__file__), "lib", "libxgboost.so")
83+ print(p if os.path.exists(p) else "")
84+ except Exception:
85+ print("")
86+ PY
87+ )
88+ if [ -n " $SO_PATH " ]; then
89+ echo " Including xgboost native lib: $SO_PATH "
90+ ADD_BINARY_ARGS+=(--add-binary " $SO_PATH :xgboost/lib" )
91+ fi
10892
109- # Copy PyInstaller files
110- cp " ${ORIGINAL_DIR} /packaging/pyinstaller/run_pyprophet.py" .
111- mkdir -p hooks
112- cp " ${ORIGINAL_DIR} /packaging/pyinstaller/hooks" /* .py hooks/ 2> /dev/null || true
93+ # Clean previous builds
94+ rm -rf build dist
95+ mkdir -p dist
11396
114- # Run PyInstaller
115- echo " Running PyInstaller..."
116- python -m PyInstaller \
97+ # Run PyInstaller with --onefile (using --collect-all for problematic packages)
98+ echo " Running PyInstaller (onefile mode)..."
99+ $PYTHON -m PyInstaller \
100+ --name pyprophet \
101+ --onefile \
102+ --console \
117103 --clean \
118104 --noconfirm \
119- --onefile \
120- --name pyprophet \
121- --strip \
122105 --log-level INFO \
123- --additional-hooks-dir hooks \
124- --exclude-module sphinx \
125- --exclude-module sphinx_rtd_theme \
126- --exclude-module pydata_sphinx_theme \
127- --exclude-module sphinx_copybutton \
128- --exclude-module sphinx.ext \
129- --exclude-module alabaster \
130- --exclude-module babel \
131- --exclude-module docutils \
132- --exclude-module mypy \
133- --exclude-module pytest \
134- --exclude-module pytest-regtest \
135- --exclude-module pytest-xdist \
136- --exclude-module black \
137- --exclude-module ruff \
106+ --additional-hooks-dir packaging/pyinstaller/hooks \
107+ --hidden-import=pyprophet \
108+ --hidden-import=pyprophet.main \
138109 --collect-submodules pyprophet \
139- --copy-metadata numpy \
140- --copy-metadata pandas \
141- --copy-metadata scipy \
142- --copy-metadata scikit-learn \
143- --copy-metadata pyopenms \
110+ --collect-all numpy \
111+ --collect-all pandas \
112+ --collect-all scipy \
113+ --collect-all sklearn \
114+ --collect-all pyopenms \
144115 --copy-metadata duckdb \
145116 --copy-metadata duckdb-extensions \
146117 --copy-metadata duckdb-extension-sqlite-scanner \
118+ --copy-metadata pyopenms \
147119 " ${ADD_BINARY_ARGS[@]} " \
148- run_pyprophet.py
149-
150- # Move executable
151- mv dist/pyprophet " ${ORIGINAL_DIR} /dist/pyprophet"
152- cd " ${ORIGINAL_DIR} "
153-
154- # Cleanup
155- deactivate
156- rm -rf " $( dirname " $VENV_DIR " ) " " ${BUILD_DIR} " /tmp/pyprophet_wheels
120+ packaging/pyinstaller/run_pyprophet.py
157121
158122echo " ============================================"
159- echo " Build complete! Single executable at: dist/pyprophet"
123+ echo " Build complete! Executable at: dist/pyprophet"
160124ls -lh dist/pyprophet
161125echo " ============================================"
162126
163- # Create archive
127+ # Create compressed archive for distribution
128+ echo " Creating distribution archive..."
164129cd dist
165130ARCH=$( uname -m)
166131ARCHIVE_NAME=" pyprophet-linux-${ARCH} .tar.gz"
167132tar -czf " ../${ARCHIVE_NAME} " pyprophet
168133cd ..
169134
135+ echo " ============================================"
170136echo " Archive created: ${ARCHIVE_NAME} "
137+ echo " ============================================"
171138
172- # Generate checksum
139+ # Generate SHA256 checksum
173140if command -v sha256sum & > /dev/null; then
174141 sha256sum " ${ARCHIVE_NAME} " > " ${ARCHIVE_NAME} .sha256"
142+ echo " Checksum: ${ARCHIVE_NAME} .sha256"
175143 cat " ${ARCHIVE_NAME} .sha256"
176144fi
177145
178146echo " "
179- echo " Test with: ./dist/pyprophet --help"
147+ echo " To test locally:"
148+ echo " ./dist/pyprophet --help"
0 commit comments