Skip to content

Commit 9c9b34b

Browse files
committed
Add Linux bundled Python support
1 parent 59cf110 commit 9c9b34b

6 files changed

Lines changed: 235 additions & 89 deletions

File tree

.github/workflows/build-linux.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ on:
55
branches:
66
- master
77
- release_v*
8+
- amorris/python-deployment
89
tags:
910
- '*'
1011
pull_request:
@@ -88,7 +89,7 @@ jobs:
8889

8990
- name: cmake
9091
shell: bash -l {0}
91-
run: conda activate shapeworks && mkdir build && cd build && cmake -DCMAKE_CXX_FLAGS=-g -DITK_DIR=$HOME/install/lib/cmake/ITK-5.4 -DVTK_DIR=$HOME/install/lib/cmake/vtk-9.5 -DXLNT_DIR=$HOME/install -DLIBIGL_DIR=$HOME/install -DOpenVDB_DIR=$HOME/install/lib/cmake/OpenVDB -DGEOMETRYCENTRAL_DIR=$HOME/install -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBuild_Studio=ON -DJKQTCommonSharedLib_DIR=$HOME/install/lib/cmake/JKQTCommonSharedLib -DJKQTMathTextSharedLib_DIR=$HOME/install/lib/cmake/JKQTMathTextSharedLib -DJKQTPlotterSharedLib_DIR=$HOME/install/lib/cmake/JKQTPlotterSharedLib -DACVD_DIR=$HOME/install -DCMAKE_PREFIX_PATH=${CONDA_PREFIX} -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/shapeworks-install -DUSE_ORIGIN_RPATH=ON -DGA_MEASUREMENT_ID=$GA_MEASUREMENT_ID -DGA_API_SECRET=$GA_API_SECRET ..
92+
run: conda activate shapeworks && mkdir build && cd build && cmake -DCMAKE_CXX_FLAGS=-g -DITK_DIR=$HOME/install/lib/cmake/ITK-5.4 -DVTK_DIR=$HOME/install/lib/cmake/vtk-9.5 -DXLNT_DIR=$HOME/install -DLIBIGL_DIR=$HOME/install -DOpenVDB_DIR=$HOME/install/lib/cmake/OpenVDB -DGEOMETRYCENTRAL_DIR=$HOME/install -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DBuild_Studio=ON -DJKQTCommonSharedLib_DIR=$HOME/install/lib/cmake/JKQTCommonSharedLib -DJKQTMathTextSharedLib_DIR=$HOME/install/lib/cmake/JKQTMathTextSharedLib -DJKQTPlotterSharedLib_DIR=$HOME/install/lib/cmake/JKQTPlotterSharedLib -DACVD_DIR=$HOME/install -DCMAKE_PREFIX_PATH=${CONDA_PREFIX} -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/shapeworks-install -DUSE_ORIGIN_RPATH=ON -DUSE_BUNDLED_PYTHON=ON -DGA_MEASUREMENT_ID=$GA_MEASUREMENT_ID -DGA_API_SECRET=$GA_API_SECRET ..
9293

9394
- name: Check space5
9495
run: df -h
@@ -97,6 +98,10 @@ jobs:
9798
shell: bash -l {0}
9899
run: conda activate shapeworks && cd build && make -j4
99100

101+
- name: Install bundled Python packages
102+
shell: bash -l {0}
103+
run: conda activate shapeworks && cd build && make bundled_pip_install
104+
100105
- name: Check space6
101106
run: df -h
102107

Libs/Application/Job/PythonWorker.cpp

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -504,32 +504,45 @@ bool PythonWorker::install_torch(std::function<void(std::string)> output_callbac
504504

505505
//---------------------------------------------------------------------------
506506
std::string PythonWorker::find_bundled_python_home() {
507-
// On macOS .app: applicationDirPath() = .../Contents/MacOS
508-
// So ../Resources/Python reaches the bundled Python
507+
// Application directory varies by platform and context:
508+
// - macOS .app: .../Contents/MacOS
509+
// - Linux installed: .../bin
510+
// - Build tree (macOS .app): .../bin/ShapeWorksStudio.app/Contents/MacOS
511+
// - Build tree (CLI/Linux): .../bin
509512
QString app_dir = QCoreApplication::applicationDirPath();
510513
QDir dir(app_dir);
511514

512-
// Try macOS .app bundle layout
513-
QString candidate = dir.absoluteFilePath("../Resources/Python");
514-
QDir candidate_dir(candidate);
515-
QString canonical = candidate_dir.canonicalPath();
515+
// Candidates for installed bundles (in priority order)
516+
QStringList installed_candidates = {
517+
#ifdef __APPLE__
518+
// macOS .app bundle: ../Resources/Python relative to Contents/MacOS
519+
dir.absoluteFilePath("../Resources/Python"),
520+
#else
521+
// Linux installed: ../lib/python relative to bin/
522+
dir.absoluteFilePath("../lib/python"),
523+
#endif
524+
};
516525

517-
if (!canonical.isEmpty() && QFile::exists(canonical + "/lib/python3.12/os.py")) {
518-
return canonical.toStdString();
526+
for (const auto& c : installed_candidates) {
527+
QDir candidate_dir(c);
528+
QString canonical = candidate_dir.canonicalPath();
529+
if (!canonical.isEmpty() && QFile::exists(canonical + "/lib/python3.12/os.py")) {
530+
return canonical.toStdString();
531+
}
519532
}
520533

521534
// Try build-tree layouts.
522535
// Studio .app: executable is in bin/ShapeWorksStudio.app/Contents/MacOS/
523-
// CLI: executable is in bin/
536+
// CLI/Linux: executable is in bin/
524537
// Bundled Python is in _bundled_python/python/ (sibling to bin/)
525538
QStringList build_tree_candidates = {
526-
dir.absoluteFilePath("../../../../_bundled_python/python"), // .app bundle
527-
dir.absoluteFilePath("../_bundled_python/python"), // CLI executable
539+
dir.absoluteFilePath("../../../../_bundled_python/python"), // macOS .app bundle
540+
dir.absoluteFilePath("../_bundled_python/python"), // CLI executable or Linux
528541
};
529542

530543
for (const auto& c : build_tree_candidates) {
531-
candidate_dir = QDir(c);
532-
canonical = candidate_dir.canonicalPath();
544+
QDir candidate_dir(c);
545+
QString canonical = candidate_dir.canonicalPath();
533546
if (!canonical.isEmpty() && QFile::exists(canonical + "/lib/python3.12/os.py")) {
534547
return canonical.toStdString();
535548
}

Studio/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,17 @@ IF(APPLE)
359359
)
360360
ENDIF(APPLE)
361361

362+
# Linux: Add RPATH for bundled Python library
363+
if(UNIX AND NOT APPLE)
364+
if(USE_BUNDLED_PYTHON)
365+
set_property(
366+
TARGET ShapeWorksStudio
367+
APPEND PROPERTY INSTALL_RPATH
368+
"$ORIGIN/../lib/python/lib"
369+
)
370+
endif()
371+
endif()
372+
362373
TARGET_LINK_LIBRARIES(ShapeWorksStudio
363374
PUBLIC
364375
Qt5::Widgets

Support/package.sh

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,21 +143,48 @@ if [[ "$OSTYPE" == "darwin"* ]]; then
143143
rm lib/*.a
144144

145145
else
146-
# Copy libraries from anaconda
147-
conda_libs="libboost_iostreams libboost_filesystem libbz2 liblzma libtbb libHalf libpython libz libspd"
148-
for clib in $conda_libs; do
149-
cp ${CONDA_PREFIX}/lib/${clib}* lib
150-
done
146+
# Linux packaging
147+
148+
# Check if bundled Python is present
149+
if [ -d "lib/python" ]; then
150+
echo "Bundled Python detected — skipping conda libpython copy"
151+
152+
# Copy non-python conda libs
153+
conda_libs="libboost_iostreams libboost_filesystem libbz2 liblzma libtbb libHalf libz libspd"
154+
for clib in $conda_libs; do
155+
cp ${CONDA_PREFIX}/lib/${clib}* lib 2>/dev/null || true
156+
done
157+
158+
# Fix RPATH on shapeworks_py.so in bundled site-packages
159+
PYBIND_SO=$(find lib/python -name "shapeworks_py*.so" 2>/dev/null | head -1)
160+
if [ -n "$PYBIND_SO" ] && command -v patchelf &> /dev/null; then
161+
echo "Setting RPATH on bundled $PYBIND_SO"
162+
# From site-packages, need to reach lib/ for dependencies
163+
patchelf --set-rpath '$ORIGIN/../../../../lib:$ORIGIN/../../../../../lib' "$PYBIND_SO" || echo ok
164+
fi
165+
166+
# Fix RPATH on bundled python3 executable
167+
if [ -f "lib/python/bin/python3" ] && command -v patchelf &> /dev/null; then
168+
echo "Setting RPATH on bundled python3"
169+
patchelf --set-rpath '$ORIGIN/../lib' lib/python/bin/python3 || echo ok
170+
fi
171+
else
172+
# Copy libraries from anaconda (legacy conda-based workflow)
173+
conda_libs="libboost_iostreams libboost_filesystem libbz2 liblzma libtbb libHalf libpython libz libspd"
174+
for clib in $conda_libs; do
175+
cp ${CONDA_PREFIX}/lib/${clib}* lib 2>/dev/null || true
176+
done
177+
fi
151178

152179
# remove static libs
153-
rm lib/*.a
154-
180+
rm -f lib/*.a
181+
155182
cd bin
156183
linuxdeployqt ShapeWorksStudio -verbose=2
157184
cd ..
158-
185+
159186
# Keep libfreetype as harfbuzz depends on it (FT_Get_Transform requires freetype 2.11+)
160-
rm lib/libxcb* lib/libX* lib/libfont*
187+
rm -f lib/libxcb* lib/libX* lib/libfont*
161188
rm -rf geometry-central doc
162189
fi
163190

cmake/BundledPython.cmake

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,8 @@ set(_bundled_dir "${CMAKE_BINARY_DIR}/_bundled_python")
3939
set(_archive_path "${_bundled_dir}/${_archive_name}")
4040
set(BUNDLED_PYTHON_ROOT "${_bundled_dir}/python" CACHE PATH "Root of bundled standalone Python" FORCE)
4141

42-
if(APPLE)
43-
set(BUNDLED_PYTHON_EXECUTABLE "${BUNDLED_PYTHON_ROOT}/bin/python3" CACHE FILEPATH "Bundled Python executable" FORCE)
44-
else()
45-
set(BUNDLED_PYTHON_EXECUTABLE "${BUNDLED_PYTHON_ROOT}/bin/python3" CACHE FILEPATH "Bundled Python executable" FORCE)
46-
endif()
42+
# Python executable is in bin/ on all Unix platforms
43+
set(BUNDLED_PYTHON_EXECUTABLE "${BUNDLED_PYTHON_ROOT}/bin/python3" CACHE FILEPATH "Bundled Python executable" FORCE)
4744

4845
# ---------------------------------------------------------------------------
4946
# Download + extract (only if not already present)

0 commit comments

Comments
 (0)