File OrcaSlicer.spec of Package OrcaSlicer

#
# spec file for package OrcaSlicer
#
# Copyright (c) 2026 SUSE LLC and contributors
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.

# Please submit bugfixes or comments via https://bugs.opensuse.org/
#


%define __builder ninja
Name:           OrcaSlicer
Version:        2.3.1
Release:        0
Summary:        G-code generator for FDM 3D printers
License:        AGPL-3.0-only
URL:            https://github.com/SoftFever/OrcaSlicer
Source0:        OrcaSlicer-%{version}.tar.gz
Source1:        https://github.com/SoftFever/Orca-deps-libnoise/archive/refs/tags/1.0.tar.gz#/libnoise-1.0.tar.gz
# Build requirements (following SuperSlicer pattern)
BuildRequires:  blosc-devel
BuildRequires:  cereal-devel
BuildRequires:  cgal-devel
BuildRequires:  cmake
BuildRequires:  dbus-1-devel
BuildRequires:  eigen3-devel >= 3
BuildRequires:  expat
BuildRequires:  fdupes
BuildRequires:  gcc-c++
BuildRequires:  gettext-tools
BuildRequires:  glew-devel
BuildRequires:  gstreamer-devel
BuildRequires:  gtest >= 1.7
BuildRequires:  hicolor-icon-theme
BuildRequires:  ilmbase-devel
BuildRequires:  libbgcode-devel
BuildRequires:  libboost_atomic-devel
BuildRequires:  libboost_chrono-devel
BuildRequires:  libboost_date_time-devel
BuildRequires:  libboost_filesystem-devel
BuildRequires:  libboost_iostreams-devel
BuildRequires:  libboost_locale-devel
BuildRequires:  libboost_log-devel
BuildRequires:  libboost_nowide-devel
BuildRequires:  libboost_program_options-devel
BuildRequires:  libboost_regex-devel
BuildRequires:  libboost_thread-devel
BuildRequires:  libcurl-devel
BuildRequires:  libexpat-devel
BuildRequires:  libglfw-devel
BuildRequires:  libjpeg8-devel
BuildRequires:  libpng16-devel
BuildRequires:  libsecret-devel
BuildRequires:  libudev-devel
BuildRequires:  memory-constraints
BuildRequires:  nanosvg-devel
BuildRequires:  ninja
BuildRequires:  nlopt-devel
BuildRequires:  opencv-devel
BuildRequires:  openvdb-devel >= 7.1
BuildRequires:  openssl-devel
BuildRequires:  pkgconfig
BuildRequires:  qhull_r-devel
BuildRequires:  qhullcpp-devel-static
BuildRequires:  tbb-devel
BuildRequires:  update-desktop-files
BuildRequires:  webkit2gtk3-devel
BuildRequires:  wxGTK3-devel >= 3.2
BuildRequires:  zlib-devel-static
BuildRequires:  cmake(OpenCASCADE)
BuildRequires:  pkgconfig(wayland-client)
BuildRequires:  pkgconfig(wayland-egl)

%description
OrcaSlicer is an open-source slicer for FDM (Fused Deposition Modeling)
3D printers. It converts 3D models (STL, OBJ, AMF, 3MF, STEP) into G-code
instructions for 3D printers.

OrcaSlicer is a fork of Bambu Studio by Bambulab, which itself is based
on PrusaSlicer by Prusa Research. It supports many printer brands including
Bambu Lab, Prusa, Voron, Creality, and many more.

%prep
%autosetup -p1 -n %{name}-%{version}

# Extract and build libnoise
tar -xzf %{SOURCE1}
pushd Orca-deps-libnoise-1.0
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=%{_builddir}/%{name}-%{version}/libnoise-install \
         -DCMAKE_BUILD_TYPE=Release \
         -DCMAKE_POLICY_VERSION_MINIMUM=3.5
make %{?_smp_mflags}
make install
popd

%if 0%{?suse_version}
sed -i 's/+UNKNOWN/%{release}-%{?is_opensuse:open}SUSE-0%{?suse_version}/' version.inc
%endif

# ===== Boost fixes =====
# Use FindBoost module instead of BoostConfig.cmake
sed -i 's/cmake_policy(SET CMP0167 NEW)/cmake_policy(SET CMP0167 OLD)/' CMakeLists.txt
sed -i '/find_package(Boost/i set(Boost_NO_BOOST_CMAKE ON)' CMakeLists.txt
# Lower Boost version and remove system component
sed -i 's/find_package(Boost 1.83.0 REQUIRED COMPONENTS system filesystem/find_package(Boost 1.66.0 REQUIRED COMPONENTS filesystem/' CMakeLists.txt

# ===== OCCT library names (new OpenCASCADE) =====
sed -i 's/TKXDESTEP/TKDESTEP/' src/libslic3r/CMakeLists.txt
sed -i '/TKSTEP$/d' src/libslic3r/CMakeLists.txt
sed -i '/TKSTEP209/d' src/libslic3r/CMakeLists.txt
sed -i '/TKSTEPAttr/d' src/libslic3r/CMakeLists.txt
sed -i '/TKSTEPBase/d' src/libslic3r/CMakeLists.txt
sed -i '/TKXSBase/d' src/libslic3r/CMakeLists.txt

# ===== OpenCV library names =====
sed -i 's/opencv_world/opencv_imgproc opencv_core/' src/libslic3r/CMakeLists.txt

# ===== Qhull =====
sed -i "s|find_package(Qhull 7.2 REQUIRED)|find_package(Qhull 8.0.2 REQUIRED)|" src/CMakeLists.txt
sed -i 's#INTERFACE Qhull::qhullcpp#INTERFACE -lqhullcpp#' src/CMakeLists.txt

# ===== CGAL 6 API fixes =====
# AABB_traits -> AABB_traits_3
sed -i 's/CGAL::AABB_traits</CGAL::AABB_traits_3</g' src/libslic3r/CutSurface.cpp
# property_map returns std::optional in CGAL 6, not std::pair
sed -i 's/\.property_map<\([^>]*\)>(\([^)]*\))\.first/.property_map<\1>(\2).value()/g' src/libslic3r/CutSurface.cpp

# ===== Boost.Process removed in Boost 1.90+ - use popen() instead =====
# Remove boost/process.hpp include and namespace alias
sed -i '/#include <boost\/process.hpp>/d' src/libslic3r/GCode/PostProcessor.cpp
sed -i '/namespace process = boost::process;/d' src/libslic3r/GCode/PostProcessor.cpp
# Add sys/wait.h for WEXITSTATUS
sed -i '/#include <cstdlib>/a #include <sys/wait.h>' src/libslic3r/GCode/PostProcessor.cpp
# Replace process::ipstream and process::child with popen-based code
sed -i '/process::ipstream istd_err;/,/return child.exit_code();/c\
    std::string full_cmd = std::string(shell) + " -c '\"'" + command + "'\"' 2>\&1";\
    FILE* pipe = popen(full_cmd.c_str(), "r");\
    if (!pipe) {\
        std_err = "Failed to execute post-processing script: popen failed";\
        return -1;\
    }\
    std_err.clear();\
    char buffer[256];\
    while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {\
        std_err.append(buffer);\
    }\
    int status = pclose(pipe);\
    return WIFEXITED(status) ? WEXITSTATUS(status) : -1;' src/libslic3r/GCode/PostProcessor.cpp

# ===== Boost.Asio io_service -> io_context (deprecated in Boost 1.90) =====
sed -i 's/boost::asio::io_service/boost::asio::io_context/g' src/slic3r/GUI/HttpServer.hpp
sed -i 's/boost::asio::io_service/boost::asio::io_context/g' src/slic3r/Utils/Bonjour.hpp
sed -i 's/boost::asio::io_service/boost::asio::io_context/g' src/slic3r/Utils/Bonjour.cpp

# ===== Fix show_profile_info() call - missing bool argument =====
sed -i 's/show_profile_info(wxString::FromUTF8/show_profile_info(true, wxString::FromUTF8/g' src/slic3r/GUI/CalibrationWizardCaliPage.cpp

# ===== Fix wxString to bool implicit conversion (wxNO_UNSAFE_WXSTRING_CONV) =====
sed -i 's/if(m_create_type\.create_nozzle)/if(!m_create_type.create_nozzle.IsEmpty())/g' src/slic3r/GUI/CreatePresetsDialog.cpp

# ===== Fix boost::process removal in Boost 1.90 =====
# MediaPlayCtrl.cpp - disable stream service on Linux (requires boost::process)
# The start_stream_service function uses boost::process which was removed in Boost 1.90
# Wrap the boost::process code in Windows-only preprocessor guards
sed -i 's|#include <boost/process.hpp>|#ifdef __WXMSW__\n#include <boost/process.hpp>\n#endif|' src/slic3r/GUI/MediaPlayCtrl.cpp

# Add return false for non-Windows before boost::process::pipe usage (line ~756)
sed -i 's|boost::process::pipe intermediate;|#ifndef __WXMSW__\n        // boost::process not available on Linux with Boost 1.90+\n        return false;\n#else\n        boost::process::pipe intermediate;|' src/slic3r/GUI/MediaPlayCtrl.cpp

# Close the #ifdef __WXMSW__ block after process_ffmpeg.detach()
sed -i 's|process_ffmpeg.detach();|process_ffmpeg.detach();\n#endif // __WXMSW__|' src/slic3r/GUI/MediaPlayCtrl.cpp

# ===== Fix wxArrayString constructor - can't take (int, std::string) =====
# DeviceManager.cpp line 5050: wxArrayString dev_id_list(1, dev_id_string);
# Change to: wxArrayString dev_id_list; dev_id_list.Add(dev_id_string);
sed -i 's/wxArrayString\s*dev_id_list(1,\s*dev_id_string);/wxArrayString dev_id_list; dev_id_list.Add(dev_id_string);/' src/slic3r/GUI/DeviceManager.cpp

# Plater.cpp:9396 - wxArrayString(1, target_path.string()) is invalid
# Replace with initializer list which works with wxArrayString
sed -i 's/wxArrayString(1, target_path\.string())/wxArrayString{wxString::FromUTF8(target_path.string())}/' src/slic3r/GUI/Plater.cpp

# PhysicalPrinterDialog.cpp:376 - ambiguous set_values call
# Explicitly use std::vector<std::string> to disambiguate
sed -i 's/choice->set_values({ m_config->opt_string("printhost_port") })/choice->set_values(std::vector<std::string>{ m_config->opt_string("printhost_port") })/' src/slic3r/GUI/PhysicalPrinterDialog.cpp

# ===== Fix wxList Node -> compatibility_iterator (wxWidgets 3.2) =====
# SendMultiMachinePage.cpp - wxList now uses compatibility_iterator instead of Node
sed -i 's/AmsRadioSelectorList::Node\*/AmsRadioSelectorList::compatibility_iterator /g' src/slic3r/GUI/SendMultiMachinePage.cpp

# ===== Fix Bonjour.cpp Boost.Asio API changes (Boost 1.90) =====
# 1. io_context->post() -> boost::asio::post(*io_context, ...)
sed -i 's|io_service->post(\(.*\));|boost::asio::post(*io_service, \1);|g' src/slic3r/Utils/Bonjour.cpp

# 2. asio::deadline_timer -> boost::asio::steady_timer
sed -i 's|asio::deadline_timer|boost::asio::steady_timer|g' src/slic3r/Utils/Bonjour.cpp

# 3. expires_from_now(boost::posix_time::seconds(...)) -> expires_after(std::chrono::seconds(...))
sed -i 's|expires_from_now(boost::posix_time::seconds(\([^)]*\)))|expires_after(std::chrono::seconds(\1))|g' src/slic3r/Utils/Bonjour.cpp

# 4. Add chrono header if not present
sed -i '/#include <boost\/asio\.hpp>/a #include <chrono>' src/slic3r/Utils/Bonjour.cpp

# ===== Fix Serial.hpp/cpp io_service -> io_context (Boost 1.90) =====
sed -i 's/boost::asio::io_service/boost::asio::io_context/g' src/slic3r/Utils/Serial.hpp
sed -i 's/asio::io_service/boost::asio::io_context/g' src/slic3r/Utils/Serial.cpp

# ===== Fix TCPConsole.cpp resolver_results API (Boost 1.90) =====
# endpoints->endpoint() is no longer valid, use endpoints.begin()->endpoint()
sed -i 's/endpoints->endpoint()/endpoints.begin()->endpoint()/g' src/slic3r/Utils/TCPConsole.cpp

# RemovableDriveManager.cpp - replace boost::process with POSIX popen()
# First replace the include
sed -i 's|#include <boost/process.hpp>|// boost::process removed in Boost 1.90 - using POSIX alternatives\n#include <cstdio>\n#include <sys/wait.h>|' src/slic3r/GUI/RemovableDriveManager.cpp

# Replace boost::process::ipstream with our popen setup
sed -i 's|boost::process::ipstream istd_err;|// Using POSIX popen() instead of boost::process\n\t\tstd::string cmd;|' src/slic3r/GUI/RemovableDriveManager.cpp

# Replace the boost::process::child( line - just remove it
sed -i 's|boost::process::child child(||' src/slic3r/GUI/RemovableDriveManager.cpp

# Replace diskutil line (macOS)
sed -i "s|boost::process::search_path(\"diskutil\"), \"eject\", correct_path.c_str(), (boost::process::std_out \& boost::process::std_err) > istd_err);|cmd = \"diskutil eject '\" + correct_path + \"' 2>\&1\";|" src/slic3r/GUI/RemovableDriveManager.cpp

# Replace umount line (Linux)
sed -i "s|boost::process::search_path(\"umount\"), correct_path.c_str(), (boost::process::std_out \& boost::process::std_err) > istd_err);|cmd = \"umount '\" + correct_path + \"' 2>\&1\";\n\t\tFILE* pipe = popen(cmd.c_str(), \"r\");\n\t\tif (pipe) { char buffer[256]; while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { BOOST_LOG_TRIVIAL(trace) << buffer; } }\n\t\tint ret = pipe ? pclose(pipe) : -1;|" src/slic3r/GUI/RemovableDriveManager.cpp

# Replace the while loop (child.running()) - make it empty
sed -i 's|while (child.running() && std::getline(istd_err, line))|while (false)|' src/slic3r/GUI/RemovableDriveManager.cpp

# Replace child.wait(ec); - no longer needed
sed -i 's|child.wait(ec);|// pclose already waited for completion|' src/slic3r/GUI/RemovableDriveManager.cpp

# Replace if (ec) check with ret == -1 check
sed -i 's|if (ec) {|if (ret == -1) {|' src/slic3r/GUI/RemovableDriveManager.cpp

# Replace child.exit_code() with WEXITSTATUS
sed -i 's|int err = child.exit_code();|int err = WIFEXITED(ret) ? WEXITSTATUS(ret) : -1;|' src/slic3r/GUI/RemovableDriveManager.cpp

# Fix the error message
sed -i 's|boost::process::child::wait() failed during Ejection|popen/pclose failed during Ejection|' src/slic3r/GUI/RemovableDriveManager.cpp
sed -i 's|ec.value()|ret|' src/slic3r/GUI/RemovableDriveManager.cpp

# ===== Add X11 library to OrcaSlicer target (needed for XInitThreads) =====
# Add find_package(X11) to MAIN CMakeLists.txt (not src/CMakeLists.txt) so target is available
sed -i '/find_package(GLEW REQUIRED)/a find_package(X11 REQUIRED)' CMakeLists.txt
# Link X11 to OrcaSlicer using the imported target
sed -i '/target_link_libraries(OrcaSlicer libslic3r_gui)/s/)$/ X11::X11)/' src/CMakeLists.txt

# ===== Add webkit2gtk-4.1 library (needed for WebView.cpp) =====
# pkg-config finds webkit2gtk-4.1 headers but library not linked
sed -i '/target_link_libraries(OrcaSlicer libslic3r_gui/s/)$/ webkit2gtk-4.1)/' src/CMakeLists.txt

%build
%limit_build -m 6144

# Add flags to suppress deprecated warnings from CGAL
export CXXFLAGS="${CXXFLAGS} -Wno-deprecated-declarations -Wno-error=deprecated-declarations -Wno-cpp -DCGAL_NO_DEPRECATION_WARNINGS"

%cmake -Wno-dev -DSLIC3R_FHS=1 \
       -DSLIC3R_BUILD_TESTS=0 \
       -DSLIC3R_GTK=3 \
       -DOPENVDB_FIND_MODULE_PATH=%{_libdir}/cmake/OpenVDB \
       -DLIBNOISE_INCLUDE_DIR=%{_builddir}/%{name}-%{version}/libnoise-install/include \
       -DLIBNOISE_LIBRARY=%{_builddir}/%{name}-%{version}/libnoise-install/lib64/liblibnoise_static.a \
%ifarch i686 i586 i386
       -DCMAKE_C_FLAGS:STRING="%{optflags} -mfpmath=sse -msse2" \
       -DCMAKE_CXX_FLAGS:STRING="%{optflags} -mfpmath=sse -msse2"
%endif

%cmake_build

# Create i18n output directories before generating .mo files
for lang in ca cs de en es fr hu it ja ko lt nl pl pt_BR ru sv tr uk zh_CN zh_TW; do
    mkdir -p resources/i18n/$lang
done

%cmake_build gettext_po_to_mo

%install
%cmake_install

%if 0%{?suse_version} > 1500
    %suse_update_desktop_file -i %{name}
%else
    %suse_update_desktop_file -i -r %{name} Graphics 3DGraphics
%endif

rm -rf %{buildroot}%{_includedir}
rm -rf %{buildroot}%{_prefix}/lib/cmake

# Remove localization source files
find %{buildroot}%{_datadir}/%{name}/i18n/ -name \*.po -delete 2>/dev/null || true
find %{buildroot}%{_datadir}/%{name}/i18n/ -name settings.ini -delete 2>/dev/null || true

# Remove LICENSE.txt installed to wrong location
rm -f %{buildroot}/usr/LICENSE.txt

# Handle locale files
find %{buildroot}%{_datadir}/%{name}/i18n -type f -o -type l 2>/dev/null | sed '
    s:'"%{buildroot}"'::
    s:\(.*/%{name}/i18n/\)\([^/_]\+\)\(.*\.mo$\):%%lang(\2) \1\2\3:
    s:^\([^%].*\)::
    s:%lang(C) ::
    /^$/d
' > lang-files || touch lang-files

find %{buildroot}%{_datadir}/%{name}/i18n -type d 2>/dev/null | sed '
    s:'"%{buildroot}"'::
    s:\(.*\):%dir \1:
' >> lang-files || true

%fdupes %{buildroot}%{_datadir}

%files -f lang-files
%{_bindir}/orca-slicer
%dir %{_datadir}/%{name}/
%{_datadir}/%{name}/
%{_datadir}/icons/hicolor/*/apps/%{name}.png
%{_datadir}/applications/%{name}.desktop
%license LICENSE.txt
%doc README.md

%changelog
openSUSE Build Service is sponsored by