This commit is contained in:
PJarczak 2026-04-20 18:23:24 +02:00
parent 2711845b21
commit 52a3d0e556
17 changed files with 1005 additions and 36 deletions

239
.github/workflows/build_macos_bridge.yml vendored Normal file
View file

@ -0,0 +1,239 @@
name: Build macOS with Linux bridge runtime
on:
workflow_call:
workflow_dispatch:
jobs:
prepare_linux_bridge_runtime:
name: Prepare Linux bridge runtime
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v6
with:
lfs: 'false'
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~4.3.0"
useLocalCache: true
useCloudCache: true
- name: Apt-Install Dependencies
uses: ./.github/actions/apt-install-deps
- name: Build Linux deps
shell: bash
run: |
set -euo pipefail
for attempt in 1 2 3; do
echo "Build Linux deps attempt $attempt"
if ./build_linux.sh -drlL; then
exit 0
fi
if [ "$attempt" -lt 3 ]; then
echo "Transient dependency download/build failure, retrying after backoff"
sleep $((attempt * 20))
fi
done
exit 1
- name: Configure linux bridge host build
shell: bash
run: |
cmake -S . -B build -G "Ninja Multi-Config" -DORCA_TOOLS=ON
- name: Build linux host runtime
shell: bash
run: |
cmake --build build --config Release --target pjarczak_bambu_linux_host
bash tools/pjarczak_bambu_linux_host/package_linux_host_runtime.sh build
tar -czvf linux_host_runtime_${{ github.sha }}.tar.gz -C tools/pjarczak_bambu_linux_host/runtime linux-x86_64
- name: Upload linux host runtime artifact
uses: actions/upload-artifact@v7
with:
name: linux_host_runtime_${{ github.sha }}
path: ./linux_host_runtime_${{ github.sha }}.tar.gz
if-no-files-found: error
build_macos_arm64:
name: Build macOS arm64
runs-on: macos-15
needs: prepare_linux_bridge_runtime
env:
ORCA_UPDATER_SIG_KEY: ${{ secrets.ORCA_UPDATER_SIG_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
lfs: 'false'
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~4.3.0"
useLocalCache: true
useCloudCache: true
- name: Download linux host runtime artifact
uses: actions/download-artifact@v8
with:
name: linux_host_runtime_${{ github.sha }}
path: ${{ github.workspace }}/linux_host_runtime_artifact
- name: Extract linux host runtime artifact on macOS arm64
shell: bash
run: |
mkdir -p "$GITHUB_WORKSPACE/tools/pjarczak_bambu_linux_host/runtime"
tar -xzf "$GITHUB_WORKSPACE/linux_host_runtime_artifact/linux_host_runtime_${{ github.sha }}.tar.gz" -C "$GITHUB_WORKSPACE/tools/pjarczak_bambu_linux_host/runtime"
- name: Install mac tools
shell: bash
run: |
brew install libtool
mkdir -p "$GITHUB_WORKSPACE/deps/build/arm64"
- name: Build macOS arm64 deps
shell: bash
run: |
./build_release_macos.sh -dx -1 -a arm64 -t 11.3
- name: Build macOS arm64 app bundle
shell: bash
run: |
./build_release_macos.sh -s -n -x -1 -a arm64 -t 11.3
- name: Pack macOS arm64 app bundle
shell: bash
run: |
tar -czvf OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}.tar.gz -C build/arm64 OrcaSlicer
- name: Upload macOS arm64 app bundle
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
path: ${{ github.workspace }}/OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}.tar.gz
if-no-files-found: error
build_macos_x86_64:
name: Build macOS x86_64
runs-on: macos-15-intel
needs: prepare_linux_bridge_runtime
env:
ORCA_UPDATER_SIG_KEY: ${{ secrets.ORCA_UPDATER_SIG_KEY }}
steps:
- name: Checkout
uses: actions/checkout@v6
with:
lfs: 'false'
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~4.3.0"
useLocalCache: true
useCloudCache: true
- name: Download linux host runtime artifact
uses: actions/download-artifact@v8
with:
name: linux_host_runtime_${{ github.sha }}
path: ${{ github.workspace }}/linux_host_runtime_artifact
- name: Extract linux host runtime artifact on macOS x86_64
shell: bash
run: |
mkdir -p "$GITHUB_WORKSPACE/tools/pjarczak_bambu_linux_host/runtime"
tar -xzf "$GITHUB_WORKSPACE/linux_host_runtime_artifact/linux_host_runtime_${{ github.sha }}.tar.gz" -C "$GITHUB_WORKSPACE/tools/pjarczak_bambu_linux_host/runtime"
- name: Install mac tools
shell: bash
run: |
brew install libtool
mkdir -p "$GITHUB_WORKSPACE/deps/build/x86_64"
- name: Build macOS x86_64 deps
shell: bash
run: |
./build_release_macos.sh -dx -1 -a x86_64 -t 11.3
- name: Build macOS x86_64 app bundle
shell: bash
run: |
./build_release_macos.sh -s -n -x -1 -a x86_64 -t 11.3
- name: Pack macOS x86_64 app bundle
shell: bash
run: |
tar -czvf OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}.tar.gz -C build/x86_64 OrcaSlicer
- name: Upload macOS x86_64 app bundle
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
path: ${{ github.workspace }}/OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}.tar.gz
if-no-files-found: error
build_macos_universal:
name: Build macOS universal
runs-on: macos-15
needs:
- build_macos_arm64
- build_macos_x86_64
steps:
- name: Checkout
uses: actions/checkout@v6
with:
lfs: 'false'
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~4.3.0"
useLocalCache: true
useCloudCache: true
- name: Download macOS app bundles
uses: actions/download-artifact@v8
with:
pattern: OrcaSlicer_Mac_bundle_*_${{ github.sha }}
path: ${{ github.workspace }}/mac_bundles
- name: Extract macOS app bundles
shell: bash
run: |
mkdir -p build/arm64 build/x86_64
arm_bundle=$(find "$GITHUB_WORKSPACE/mac_bundles/OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}" -name '*.tar.gz' -print -quit)
x86_bundle=$(find "$GITHUB_WORKSPACE/mac_bundles/OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}" -name '*.tar.gz' -print -quit)
tar -xzvf "$arm_bundle" -C "$GITHUB_WORKSPACE/build/arm64"
tar -xzvf "$x86_bundle" -C "$GITHUB_WORKSPACE/build/x86_64"
- name: Build universal mac app bundle
shell: bash
run: |
./build_release_macos.sh -u -x -1 -a universal -t 11.3
- name: Create DMG without runtime validation
shell: bash
run: |
mkdir -p "$GITHUB_WORKSPACE/build/universal/OrcaSlicer_dmg"
rm -rf "$GITHUB_WORKSPACE/build/universal/OrcaSlicer_dmg"/*
cp -R "$GITHUB_WORKSPACE/build/universal/OrcaSlicer/OrcaSlicer.app" "$GITHUB_WORKSPACE/build/universal/OrcaSlicer_dmg/"
ln -sfn /Applications "$GITHUB_WORKSPACE/build/universal/OrcaSlicer_dmg/Applications"
ver=$(grep 'set(SoftFever_VERSION' version.inc | cut -d '"' -f2)
hdiutil create -volname "OrcaSlicer" -srcfolder "$GITHUB_WORKSPACE/build/universal/OrcaSlicer_dmg" -ov -format UDZO "OrcaSlicer_Mac_universal_V${ver}.dmg"
- name: Explain skipped hosted runtime validation
shell: bash
run: |
echo "Hosted macOS runners do not support nested virtualization, so bundled Lima runtime validation is intentionally skipped here."
- name: Upload macOS universal DMG
uses: actions/upload-artifact@v7
with:
name: OrcaSlicer_Mac_universal_${{ github.sha }}
path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_V*.dmg
if-no-files-found: error

View file

@ -834,6 +834,10 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
${CMAKE_PREFIX_PATH}/bin/occt/TKXDESTEP.dll
${CMAKE_PREFIX_PATH}/bin/occt/TKXSBase.dll
${CMAKE_PREFIX_PATH}/bin/freetype.dll
${CMAKE_PREFIX_PATH}/bin/avcodec-61.dll
${CMAKE_PREFIX_PATH}/bin/swresample-5.dll
${CMAKE_PREFIX_PATH}/bin/swscale-8.dll
${CMAKE_PREFIX_PATH}/bin/avutil-59.dll
DESTINATION ${_out_dir})
set(${output_dlls}
@ -869,6 +873,10 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
${_out_dir}/TKXSBase.dll
${_out_dir}/freetype.dll
${_out_dir}/avcodec-61.dll
${_out_dir}/swresample-5.dll
${_out_dir}/swscale-8.dll
${_out_dir}/avutil-59.dll
PARENT_SCOPE
)

View file

@ -100,7 +100,7 @@ if [ -z "$CMAKE_IGNORE_PREFIX_PATH" ]; then
export CMAKE_IGNORE_PREFIX_PATH="/opt/local:/usr/local:/opt/homebrew"
fi
CMAKE_VERSION=$(cmake --version | head -1 | sed 's/[^0-9]*\([0-9]*\).*//')
CMAKE_VERSION=$(cmake --version | head -1 | sed -E 's/[^0-9]*([0-9]+).*/\1/')
if [ "$CMAKE_VERSION" -ge 4 ] 2>/dev/null; then
export CMAKE_POLICY_VERSION_MINIMUM=3.5
export CMAKE_POLICY_COMPAT="-DCMAKE_POLICY_VERSION_MINIMUM=3.5"
@ -123,6 +123,7 @@ PROJECT_BUILD_DIR="$PROJECT_DIR/build/$ARCH"
DEPS_DIR="$PROJECT_DIR/deps"
HOST_RUNTIME_DIR="$PROJECT_DIR/tools/pjarczak_bambu_linux_host/runtime/linux-x86_64"
HOST_WRAPPER="$PROJECT_DIR/tools/pjarczak_bambu_linux_host/pjarczak-bambu-linux-host-wrapper"
MAC_RUNTIME_HELPERS_DIR="$PROJECT_DIR/tools/pjarczak_bambu_runtime/macos"
export BUILD_DIR_CONFIG_SUBDIR="/$BUILD_CONFIG"
@ -154,6 +155,21 @@ copy_linux_bridge_runtime_to_app() {
exit 1
fi
if [ ! -f "$MAC_RUNTIME_HELPERS_DIR/pjarczak_install_macos_runtime.sh" ]; then
echo "Missing mac runtime installer: $MAC_RUNTIME_HELPERS_DIR/pjarczak_install_macos_runtime.sh"
exit 1
fi
if [ ! -f "$MAC_RUNTIME_HELPERS_DIR/pjarczak_verify_macos_runtime.sh" ]; then
echo "Missing mac runtime verifier: $MAC_RUNTIME_HELPERS_DIR/pjarczak_verify_macos_runtime.sh"
exit 1
fi
if [ ! -f "$MAC_RUNTIME_HELPERS_DIR/pjarczak_lima_instance.txt" ]; then
echo "Missing Lima instance file: $MAC_RUNTIME_HELPERS_DIR/pjarczak_lima_instance.txt"
exit 1
fi
if [ ! -f "$bridge_dylib" ]; then
echo "Missing installed bridge dylib: $bridge_dylib"
exit 1
@ -162,9 +178,17 @@ copy_linux_bridge_runtime_to_app() {
cp -f "$bridge_dylib" "$macos_dir/"
find "$HOST_RUNTIME_DIR" -maxdepth 1 -type f -exec cp -f {} "$macos_dir/" \;
cp -f "$HOST_WRAPPER" "$macos_dir/"
cp -f "$MAC_RUNTIME_HELPERS_DIR/pjarczak_install_macos_runtime.sh" "$macos_dir/install_runtime_macos.sh"
cp -f "$MAC_RUNTIME_HELPERS_DIR/pjarczak_verify_macos_runtime.sh" "$macos_dir/verify_runtime_macos.sh"
cp -f "$MAC_RUNTIME_HELPERS_DIR/pjarczak_lima_instance.txt" "$macos_dir/"
if [ -f "$MAC_RUNTIME_HELPERS_DIR/README_runtime_bridge.txt" ]; then
cp -f "$MAC_RUNTIME_HELPERS_DIR/README_runtime_bridge.txt" "$macos_dir/"
fi
chmod +x "$macos_dir/pjarczak_bambu_linux_host"
chmod +x "$macos_dir/pjarczak-bambu-linux-host-wrapper"
chmod +x "$macos_dir/install_runtime_macos.sh"
chmod +x "$macos_dir/verify_runtime_macos.sh"
}
build_deps() {

7
deps/CMakeLists.txt vendored
View file

@ -382,6 +382,12 @@ if(NOT FREETYPE_FOUND)
set(FREETYPE_PKG "dep_FREETYPE")
endif()
set(FFMPEG_PKG "")
if (MSVC)
include(FFMPEG/FFMPEG.cmake)
set(FFMPEG_PKG "dep_FFMPEG")
endif()
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --is-inside-work-tree
RESULT_VARIABLE REV_PARSE_RESULT
@ -415,6 +421,7 @@ set(_dep_list
dep_GLFW
dep_OCCT
${FREETYPE_PKG}
${FFMPEG_PKG}
${PNG_PKG}
${ZLIB_PKG}
${EXPAT_PKG}

14
deps/FFMPEG/FFMPEG.cmake vendored Normal file
View file

@ -0,0 +1,14 @@
set(_dstdir ${DESTDIR}/usr/local)
set(_source_dir "${CMAKE_BINARY_DIR}/dep_FFMPEG-prefix/src/dep_FFMPEG")
ExternalProject_Add(dep_FFMPEG
URL https://github.com/bambulab/ffmpeg_prebuilts/releases/download/7.0.2/7.0.2_msvc.zip
URL_HASH SHA256=DF44AE6B97CE84C720695AE7F151B4A9654915D1841C68F10D62A1189E0E7181
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/bin" "${_dstdir}/bin"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/lib" "${_dstdir}/lib"
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/include" "${_dstdir}/include"
)

View file

@ -680,21 +680,22 @@ if (APPLE)
GUI/GUI_UtilsMac.mm
GUI/wxMediaCtrl2.mm
GUI/wxMediaCtrl2.h
GUI/wxMediaCtrl3.h
)
FIND_LIBRARY(DISKARBITRATION_LIBRARY DiskArbitration)
else ()
list(APPEND SLIC3R_GUI_SOURCES
GUI/AVVideoDecoder.cpp
GUI/AVVideoDecoder.hpp
GUI/wxMediaCtrl2.cpp
GUI/wxMediaCtrl2.h
GUI/wxMediaCtrl3.cpp
GUI/wxMediaCtrl3.h
)
endif ()
if (UNIX AND NOT APPLE)
list(APPEND SLIC3R_GUI_SOURCES
GUI/AVVideoDecoder.cpp
GUI/AVVideoDecoder.hpp
GUI/wxMediaCtrl3.cpp
GUI/wxMediaCtrl3.h
GUI/Printer/gstbambusrc.c
)
endif ()
@ -748,7 +749,13 @@ target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo mini
if (MSVC)
target_link_libraries(libslic3r_gui Setupapi.lib)
find_library(FFMPEG_AVCODEC_LIB NAMES avcodec avcodec-61 PATHS ${CMAKE_PREFIX_PATH} PATH_SUFFIXES lib NO_DEFAULT_PATH)
find_library(FFMPEG_AVUTIL_LIB NAMES avutil avutil-59 PATHS ${CMAKE_PREFIX_PATH} PATH_SUFFIXES lib NO_DEFAULT_PATH)
find_library(FFMPEG_SWSCALE_LIB NAMES swscale swscale-8 PATHS ${CMAKE_PREFIX_PATH} PATH_SUFFIXES lib NO_DEFAULT_PATH)
if (NOT FFMPEG_AVCODEC_LIB OR NOT FFMPEG_AVUTIL_LIB OR NOT FFMPEG_SWSCALE_LIB)
message(FATAL_ERROR "Missing FFmpeg import libs for Windows build. Expected avcodec/avutil/swscale under ${CMAKE_PREFIX_PATH}/lib")
endif ()
target_link_libraries(libslic3r_gui Setupapi.lib ${FFMPEG_AVCODEC_LIB} ${FFMPEG_AVUTIL_LIB} ${FFMPEG_SWSCALE_LIB})
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
FIND_LIBRARY(WAYLAND_SERVER_LIBRARIES NAMES wayland-server)
FIND_LIBRARY(WAYLAND_EGL_LIBRARIES NAMES wayland-egl)

View file

@ -247,6 +247,8 @@ std::string VersionInfo::convert_short_version(std::string full_version)
static void pjarczak_verify_or_install_windows_bridge_runtime(const boost::filesystem::path& plugin_folder,
const boost::filesystem::path& plugin_cache_dir);
static void pjarczak_verify_or_install_macos_bridge_runtime(const boost::filesystem::path& plugin_folder,
const boost::filesystem::path& plugin_cache_dir);
static long pjarczak_run_hidden_windows_command(const wxString& command, wxArrayString* stdout_lines, wxArrayString* stderr_lines);
static void pjarczak_log_command_output(const char* tag, long exit_code, const wxArrayString& stdout_lines, const wxArrayString& stderr_lines);
@ -2976,13 +2978,15 @@ bool GUI_App::on_init_inner()
}
} */
copy_network_if_available();
#ifdef WIN32
if (Slic3r::PJarczakLinuxBridge::enabled()) {
const boost::filesystem::path plugin_folder = boost::filesystem::path(data_dir()) / "plugins";
const boost::filesystem::path plugin_cache_dir = boost::filesystem::path(data_dir()) / "ota" / "plugins";
#ifdef WIN32
pjarczak_verify_or_install_windows_bridge_runtime(plugin_folder, plugin_cache_dir);
}
#elif defined(__APPLE__) || defined(__WXMAC__)
pjarczak_verify_or_install_macos_bridge_runtime(plugin_folder, plugin_cache_dir);
#endif
}
on_init_network();
if (m_agent && m_agent->is_user_login()) {
@ -3180,6 +3184,19 @@ static wxString pjarczak_quote_windows_arg(const wxString& value)
return wxString::Format("\"%s\"", escaped);
}
static wxString pjarczak_quote_posix_arg(const wxString& value)
{
wxString out("'");
for (wxUniChar ch : value) {
if (ch == '\'')
out += "'\\''";
else
out += ch;
}
out += "'";
return out;
}
static long pjarczak_run_hidden_windows_command(const wxString& command, wxArrayString* stdout_lines, wxArrayString* stderr_lines)
{
#ifdef WIN32
@ -3199,6 +3216,25 @@ static long pjarczak_run_hidden_windows_command(const wxString& command, wxArray
#endif
}
static long pjarczak_run_hidden_posix_command(const wxString& command, wxArrayString* stdout_lines, wxArrayString* stderr_lines)
{
#if defined(__WXMAC__) || defined(__APPLE__) || defined(__WXGTK__)
wxArrayString local_stdout;
wxArrayString local_stderr;
long exit_code = wxExecute(command, local_stdout, local_stderr, wxEXEC_SYNC);
if (stdout_lines)
*stdout_lines = local_stdout;
if (stderr_lines)
*stderr_lines = local_stderr;
return exit_code;
#else
(void)command;
(void)stdout_lines;
(void)stderr_lines;
return -1;
#endif
}
static void pjarczak_log_command_output(const char* tag, long exit_code, const wxArrayString& stdout_lines, const wxArrayString& stderr_lines)
{
BOOST_LOG_TRIVIAL(info) << tag << ": exit_code=" << exit_code;
@ -3259,6 +3295,66 @@ static void pjarczak_verify_or_install_windows_bridge_runtime(const boost::files
#endif
}
static void pjarczak_verify_or_install_macos_bridge_runtime(const boost::filesystem::path& plugin_folder,
const boost::filesystem::path& plugin_cache_dir)
{
#if !(defined(__WXMAC__) || defined(__APPLE__))
(void)plugin_folder;
(void)plugin_cache_dir;
#else
if (!Slic3r::PJarczakLinuxBridge::enabled())
return;
const auto verify_script = plugin_folder / Slic3r::PJarczakLinuxBridge::mac_runtime_verify_script_file_name();
const auto install_script = plugin_folder / Slic3r::PJarczakLinuxBridge::mac_runtime_install_script_file_name();
if (!boost::filesystem::exists(verify_script) || !boost::filesystem::exists(install_script)) {
BOOST_LOG_TRIVIAL(warning) << "[pjarczak_runtime_setup] missing macOS verify/install script in " << plugin_folder.string();
return;
}
const wxString plugin_dir_wx = from_u8(plugin_folder.string());
const wxString cache_dir_wx = from_u8(plugin_cache_dir.string());
const wxString verify_cmd = wxString::Format(
"/bin/bash %s -PackageDir %s -PluginDir %s -PluginCacheDir %s -AllowMissingLinuxPlugin",
pjarczak_quote_posix_arg(from_u8(verify_script.string())),
pjarczak_quote_posix_arg(plugin_dir_wx),
pjarczak_quote_posix_arg(plugin_dir_wx),
pjarczak_quote_posix_arg(cache_dir_wx));
wxArrayString verify_out;
wxArrayString verify_err;
long verify_code = pjarczak_run_hidden_posix_command(verify_cmd, &verify_out, &verify_err);
pjarczak_log_command_output("[pjarczak_runtime_verify_macos]", verify_code, verify_out, verify_err);
if (verify_code == 0)
return;
wxMessageDialog prompt(
nullptr,
_L("The macOS Linux bridge runtime is not ready. OrcaSlicer can install or repair the bundled Lima runtime now."),
_L("OrcaSlicer Linux Bridge"),
wxYES_NO | wxICON_QUESTION | wxCENTRE);
if (prompt.ShowModal() != wxID_YES)
return;
const wxString install_cmd = wxString::Format(
"/bin/bash %s -PackageDir %s -PluginDir %s -PluginCacheDir %s -ReplaceExisting",
pjarczak_quote_posix_arg(from_u8(install_script.string())),
pjarczak_quote_posix_arg(plugin_dir_wx),
pjarczak_quote_posix_arg(plugin_dir_wx),
pjarczak_quote_posix_arg(cache_dir_wx));
wxArrayString install_out;
wxArrayString install_err;
long install_code = pjarczak_run_hidden_posix_command(install_cmd, &install_out, &install_err);
pjarczak_log_command_output("[pjarczak_runtime_install_macos]", install_code, install_out, install_err);
verify_out.clear();
verify_err.clear();
verify_code = pjarczak_run_hidden_posix_command(verify_cmd, &verify_out, &verify_err);
pjarczak_log_command_output("[pjarczak_runtime_verify_after_install_macos]", verify_code, verify_out, verify_err);
#endif
}
bool pjarczak_bridge_payload_ready(const boost::filesystem::path& plugin_folder, std::string* reason)
{
if (!Slic3r::PJarczakLinuxBridge::enabled()) {
@ -3271,6 +3367,22 @@ bool pjarczak_bridge_payload_ready(const boost::filesystem::path& plugin_folder,
return boost::filesystem::exists(candidate) && !boost::filesystem::is_directory(candidate);
};
#if defined(__WXMAC__) || defined(__APPLE__)
const std::string required_files[] = {
Slic3r::PJarczakLinuxBridge::bridge_network_current_dir_name(),
Slic3r::PJarczakLinuxBridge::host_executable_file_name(),
std::string("pjarczak_bambu_linux_host_abi1"),
std::string("pjarczak_bambu_linux_host_abi0"),
Slic3r::PJarczakLinuxBridge::mac_host_wrapper_file_name(),
Slic3r::PJarczakLinuxBridge::mac_runtime_install_script_file_name(),
Slic3r::PJarczakLinuxBridge::mac_runtime_verify_script_file_name(),
Slic3r::PJarczakLinuxBridge::mac_lima_instance_file_name(),
Slic3r::PJarczakLinuxBridge::linux_network_library_name(),
Slic3r::PJarczakLinuxBridge::linux_source_library_name(),
std::string("ca-certificates.crt"),
std::string("slicer_base64.cer")
};
#else
const std::string required_files[] = {
Slic3r::PJarczakLinuxBridge::bridge_network_current_dir_name(),
Slic3r::PJarczakLinuxBridge::host_executable_file_name(),
@ -3285,6 +3397,7 @@ bool pjarczak_bridge_payload_ready(const boost::filesystem::path& plugin_folder,
std::string("ca-certificates.crt"),
std::string("slicer_base64.cer")
};
#endif
for (const std::string& file_name : required_files) {
if (!has_file(file_name)) {
@ -3292,7 +3405,7 @@ bool pjarczak_bridge_payload_ready(const boost::filesystem::path& plugin_folder,
return false;
}
}
#if !(defined(__WXMAC__) || defined(__APPLE__))
if (!has_file(Slic3r::PJarczakLinuxBridge::windows_wsl_import_script_file_name()) &&
!has_file("pjarczak-install-wsl-runtime.ps1")) {
pjarczak_set_reason(reason, "missing required runtime file: " + Slic3r::PJarczakLinuxBridge::windows_wsl_import_script_file_name());
@ -3304,6 +3417,7 @@ bool pjarczak_bridge_payload_ready(const boost::filesystem::path& plugin_folder,
pjarczak_set_reason(reason, "missing required runtime file: " + Slic3r::PJarczakLinuxBridge::windows_wsl_bootstrap_script_file_name());
return false;
}
#endif
for (const std::string& file_name : {
Slic3r::PJarczakLinuxBridge::linux_network_library_name(),

View file

@ -8,13 +8,8 @@
#ifndef MediaPlayCtrl_h
#define MediaPlayCtrl_h
#if defined(__linux__) && !defined(__APPLE__)
#include "wxMediaCtrl3.h"
using BBLMediaCtrl = wxMediaCtrl3;
#else
#include "wxMediaCtrl2.h"
using BBLMediaCtrl = wxMediaCtrl2;
#endif
#include <wx/panel.h>

View file

@ -121,7 +121,7 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
CentreOnParent();
}
else {
#ifdef WIN32
#if defined(WIN32) || defined(__APPLE__) || defined(__WXMAC__)
if (Slic3r::PJarczakLinuxBridge::enabled()) {
m_external_browser_mode = true;
SetTitle(_L("Login"));
@ -187,7 +187,7 @@ ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_AN
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
Move(tmpPT);
#ifdef WIN32
#if defined(WIN32) || defined(__APPLE__) || defined(__WXMAC__)
}
#endif
}
@ -215,7 +215,7 @@ bool ZUserLogin::run() {
m_timer = new wxTimer(this, NETWORK_OFFLINE_TIMER_ID);
m_timer->Start(m_external_browser_mode ? 30000 : 8000);
#ifdef WIN32
#if defined(WIN32) || defined(__APPLE__) || defined(__WXMAC__)
if (m_external_browser_mode) {
NetworkAgent* agent = wxGetApp().getAgent();
if (agent) {

View file

@ -95,15 +95,47 @@ if (UNIX AND NOT APPLE AND EXISTS "${PJARCZAK_LINUX_HOST_DIR}/main.cpp" AND EXIS
endif()
if (APPLE)
set(PJARCZAK_MAC_WRAPPER_SCRIPT_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_macos_linux_wrapper/pjarczak_bambu_macos_linux_wrapper.sh)
set(PJARCZAK_MAC_WRAPPER_SCRIPT_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_linux_host/pjarczak-bambu-linux-host-wrapper)
set(PJARCZAK_MAC_INSTALLER_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/macos/pjarczak_install_macos_runtime.sh)
set(PJARCZAK_MAC_VERIFY_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/macos/pjarczak_verify_macos_runtime.sh)
set(PJARCZAK_MAC_INSTANCE_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/macos/pjarczak_lima_instance.txt)
set(PJARCZAK_MAC_README_SOURCE ${CMAKE_SOURCE_DIR}/tools/pjarczak_bambu_runtime/macos/README_runtime_bridge.txt)
add_custom_command(TARGET pjarczak_bambu_networking_bridge POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_MAC_WRAPPER_SCRIPT_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/pjarczak_bambu_macos_linux_wrapper.sh
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/pjarczak-bambu-linux-host-wrapper
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_MAC_INSTALLER_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/install_runtime_macos.sh
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_MAC_VERIFY_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/verify_runtime_macos.sh
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_MAC_INSTANCE_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/pjarczak_lima_instance.txt
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${PJARCZAK_MAC_README_SOURCE}
$<TARGET_FILE_DIR:pjarczak_bambu_networking_bridge>/README_runtime_bridge.txt
)
install(FILES ${PJARCZAK_MAC_WRAPPER_SCRIPT_SOURCE}
DESTINATION "."
RENAME pjarczak_bambu_macos_linux_wrapper.sh
RENAME pjarczak-bambu-linux-host-wrapper
)
install(FILES ${PJARCZAK_MAC_INSTALLER_SOURCE}
DESTINATION "."
RENAME install_runtime_macos.sh
)
install(FILES ${PJARCZAK_MAC_VERIFY_SOURCE}
DESTINATION "."
RENAME verify_runtime_macos.sh
)
install(FILES ${PJARCZAK_MAC_INSTANCE_SOURCE}
DESTINATION "."
RENAME pjarczak_lima_instance.txt
)
install(FILES ${PJARCZAK_MAC_README_SOURCE}
DESTINATION "."
RENAME README_runtime_bridge.txt
)
endif()

View file

@ -39,7 +39,9 @@ std::uint16_t read_u16_le(const unsigned char* p)
bool expected_machine_matches(std::uint16_t machine)
{
#if defined(__x86_64__) || defined(_M_X64)
#if defined(__APPLE__)
return machine == EM_X86_64 || machine == EM_AARCH64;
#elif defined(__x86_64__) || defined(_M_X64)
return machine == EM_X86_64;
#elif defined(__aarch64__)
return machine == EM_AARCH64;
@ -204,6 +206,21 @@ std::string mac_host_wrapper_file_name()
return "pjarczak-bambu-linux-host-wrapper";
}
std::string mac_lima_instance_file_name()
{
return "pjarczak_lima_instance.txt";
}
std::string mac_runtime_install_script_file_name()
{
return "install_runtime_macos.sh";
}
std::string mac_runtime_verify_script_file_name()
{
return "verify_runtime_macos.sh";
}
std::string windows_wsl_distro_file_name()
{
return "pjarczak_wsl_distro.txt";
@ -256,6 +273,9 @@ bool is_overlay_runtime_filename(const std::string& file_name)
file_name == "pjarczak_bambu_linux_host_abi1" ||
file_name == "pjarczak_bambu_linux_host_abi0" ||
file_name == mac_host_wrapper_file_name() ||
file_name == mac_lima_instance_file_name() ||
file_name == mac_runtime_install_script_file_name() ||
file_name == mac_runtime_verify_script_file_name() ||
file_name == windows_wsl_distro_file_name() ||
file_name == windows_wsl_import_script_file_name() ||
file_name == windows_wsl_validate_script_file_name() ||

View file

@ -23,6 +23,9 @@ std::string linux_network_library_name();
std::string linux_source_library_name();
std::string host_executable_file_name();
std::string mac_host_wrapper_file_name();
std::string mac_lima_instance_file_name();
std::string mac_runtime_install_script_file_name();
std::string mac_runtime_verify_script_file_name();
std::string windows_wsl_distro_file_name();
std::string windows_wsl_import_script_file_name();
std::string windows_wsl_validate_script_file_name();

View file

@ -1,9 +1,12 @@
#include "PJarczakLinuxSoBridgeLauncher.hpp"
#include "PJarczakLinuxBridgeConfig.hpp"
#include <cctype>
#include <cstdio>
#include <cstdlib>
#include <dlfcn.h>
#include <filesystem>
#include <fstream>
#include <string>
namespace Slic3r::PJarczakLinuxBridge {
@ -18,6 +21,144 @@ std::filesystem::path module_dir()
return std::filesystem::path(info.dli_fname).parent_path();
}
std::string trim_ascii(std::string value)
{
auto is_space = [](unsigned char ch) { return std::isspace(ch) != 0; };
while (!value.empty() && is_space(static_cast<unsigned char>(value.front())))
value.erase(value.begin());
while (!value.empty() && is_space(static_cast<unsigned char>(value.back())))
value.pop_back();
return value;
}
std::string read_text_file_trimmed(const std::filesystem::path& path)
{
std::ifstream in(path, std::ios::binary);
if (!in)
return {};
std::string value((std::istreambuf_iterator<char>(in)), std::istreambuf_iterator<char>());
if (value.size() >= 3 &&
static_cast<unsigned char>(value[0]) == 0xEFu &&
static_cast<unsigned char>(value[1]) == 0xBBu &&
static_cast<unsigned char>(value[2]) == 0xBFu)
value.erase(0, 3);
return trim_ascii(value);
}
std::string required_env(const char* name)
{
const char* value = std::getenv(name);
return (value && *value) ? trim_ascii(std::string(value)) : std::string();
}
std::string shell_quote(const std::string& value)
{
std::string out;
out.reserve(value.size() + 2);
out.push_back('\'');
for (char ch : value) {
if (ch == '\'')
out += "'\\''";
else
out.push_back(ch);
}
out.push_back('\'');
return out;
}
std::string run_and_capture(const std::string& command, int* exit_code = nullptr)
{
std::string output;
FILE* pipe = popen(command.c_str(), "r");
if (!pipe) {
if (exit_code)
*exit_code = -1;
return {};
}
char buffer[4096];
while (fgets(buffer, sizeof(buffer), pipe) != nullptr)
output += buffer;
const int rc = pclose(pipe);
if (exit_code)
*exit_code = rc;
return trim_ascii(output);
}
std::string configured_instance_name(const std::filesystem::path& plugin_dir)
{
const auto env_value = required_env("PJARCZAK_MAC_LIMA_INSTANCE");
if (!env_value.empty())
return env_value;
return read_text_file_trimmed(plugin_dir / mac_lima_instance_file_name());
}
std::string first_missing_runtime_file(const std::filesystem::path& plugin_dir)
{
const std::string required_files[] = {
host_executable_file_name(),
std::string("pjarczak_bambu_linux_host_abi1"),
std::string("pjarczak_bambu_linux_host_abi0"),
mac_host_wrapper_file_name(),
mac_runtime_install_script_file_name(),
mac_runtime_verify_script_file_name(),
mac_lima_instance_file_name(),
linux_network_library_name(),
linux_source_library_name(),
std::string("ca-certificates.crt"),
std::string("slicer_base64.cer")
};
for (const std::string& name : required_files) {
if (!std::filesystem::exists(plugin_dir / std::filesystem::path(name)))
return name;
}
return {};
}
bool probe_runtime_ready(const std::filesystem::path& plugin_dir, std::string* reason)
{
const auto verify_script = plugin_dir / mac_runtime_verify_script_file_name();
if (!std::filesystem::exists(verify_script)) {
if (reason)
*reason = "mac runtime verify script missing";
return false;
}
const std::string command =
std::string("/bin/bash ") + shell_quote(verify_script.string()) +
" -PackageDir " + shell_quote(plugin_dir.string()) +
" -PluginDir " + shell_quote(plugin_dir.string());
int rc = -1;
const std::string output = run_and_capture(command + " 2>&1", &rc);
if (rc == 0) {
if (reason)
reason->clear();
return true;
}
if (reason)
*reason = output.empty() ? std::string("macOS Lima runtime is not ready") : output;
return false;
}
LaunchSpec error_launch_spec(const std::string& message)
{
LaunchSpec spec;
spec.description = "mac bridge preflight error";
spec.argv = {
"/bin/sh",
"-lc",
"printf '%s\\n' " + shell_quote(message) + " 1>&2; exit 127"
};
return spec;
}
}
std::string host_executable_name()
@ -35,33 +176,52 @@ std::string launch_preflight_error()
const std::filesystem::path plugin_dir = module_dir();
if (plugin_dir.empty())
return "bridge launcher could not resolve plugin directory";
if (!std::filesystem::exists(plugin_dir / host_executable_file_name()))
return "linux host executable missing in plugin directory";
const char* runner = std::getenv("PJARCZAK_MAC_LINUX_GUEST_RUNNER");
if (!runner || !*runner)
return "PJARCZAK_MAC_LINUX_GUEST_RUNNER is not set";
const auto missing_file = first_missing_runtime_file(plugin_dir);
if (!missing_file.empty())
return "required macOS runtime file missing: " + missing_file;
const auto instance = configured_instance_name(plugin_dir);
if (instance.empty())
return "PJARCZAK_MAC_LIMA_INSTANCE is not set and pjarczak_lima_instance.txt is missing or empty";
std::string reason;
if (!probe_runtime_ready(plugin_dir, &reason))
return reason.empty() ? std::string("macOS Lima runtime is not ready") : reason;
return {};
}
LaunchSpec build_default_launch_spec()
{
const std::filesystem::path plugin_dir = module_dir();
if (plugin_dir.empty())
return error_launch_spec("bridge launcher could not resolve plugin directory");
const char* wrapper_env = std::getenv("PJARCZAK_BAMBU_HOST_WRAPPER");
const std::filesystem::path wrapper_path = (wrapper_env && *wrapper_env)
? std::filesystem::path(wrapper_env)
: (plugin_dir / mac_host_wrapper_file_name());
const auto missing_file = first_missing_runtime_file(plugin_dir);
if (!missing_file.empty())
return error_launch_spec("required macOS runtime file missing: " + missing_file);
const auto instance = configured_instance_name(plugin_dir);
if (instance.empty())
return error_launch_spec("PJARCZAK_MAC_LIMA_INSTANCE is not set and pjarczak_lima_instance.txt is missing or empty");
std::string reason;
if (!probe_runtime_ready(plugin_dir, &reason))
return error_launch_spec(reason.empty() ? std::string("macOS Lima runtime is not ready") : reason);
const std::filesystem::path wrapper_path = plugin_dir / mac_host_wrapper_file_name();
const std::filesystem::path host_path = plugin_dir / host_executable_file_name();
LaunchSpec spec;
spec.description = "mac via external linux guest wrapper";
spec.description = "macOS via Lima linux guest";
spec.argv = {wrapper_path.string(), host_path.string()};
spec.env = {
{"PJARCZAK_BAMBU_PLUGIN_DIR", plugin_dir.string()},
{"PJARCZAK_BAMBU_NETWORK_SO", (plugin_dir / linux_network_library_name()).string()},
{"PJARCZAK_BAMBU_SOURCE_SO", (plugin_dir / linux_source_library_name()).string()},
{"PJARCZAK_BAMBU_REQUIRE_LINUX_GUEST", "1"}
{"PJARCZAK_BAMBU_REQUIRE_LINUX_GUEST", "1"},
{"PJARCZAK_MAC_LIMA_INSTANCE", instance}
};
return spec;
}

View file

@ -7,10 +7,62 @@ if [ -z "$HOST_PATH" ]; then
exit 127
fi
RUNNER="${PJARCZAK_MAC_LINUX_GUEST_RUNNER:-}"
if [ -z "$RUNNER" ]; then
echo "PJARCZAK_MAC_LINUX_GUEST_RUNNER not set" >&2
PLUGIN_DIR="${PJARCZAK_BAMBU_PLUGIN_DIR:-}"
if [ -z "$PLUGIN_DIR" ]; then
PLUGIN_DIR=$(cd "$(dirname "$HOST_PATH")" && pwd)
fi
trim_file() {
path="$1"
if [ ! -f "$path" ]; then
return 1
fi
LC_ALL=C tr -d '\r' < "$path" | head -n 1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
}
INSTANCE="${PJARCZAK_MAC_LIMA_INSTANCE:-}"
if [ -z "$INSTANCE" ]; then
INSTANCE=$(trim_file "$PLUGIN_DIR/pjarczak_lima_instance.txt" || true)
fi
if [ -z "$INSTANCE" ]; then
INSTANCE="orcaslicer-bambu-network"
fi
find_limactl() {
if [ -n "${PJARCZAK_LIMACTL:-}" ] && [ -x "${PJARCZAK_LIMACTL}" ]; then
printf '%s\n' "$PJARCZAK_LIMACTL"
return 0
fi
if command -v limactl >/dev/null 2>&1; then
command -v limactl
return 0
fi
for candidate in \
"$HOME/Library/Application Support/OrcaSlicer/macos-bridge/lima/bin/limactl" \
"/opt/homebrew/bin/limactl" \
"/usr/local/bin/limactl"
do
if [ -x "$candidate" ]; then
printf '%s\n' "$candidate"
return 0
fi
done
return 1
}
LIMACTL=$(find_limactl || true)
if [ -z "$LIMACTL" ]; then
echo "limactl not found" >&2
exit 127
fi
exec "$RUNNER" "$HOST_PATH"
exec "$LIMACTL" shell "$INSTANCE" -- /usr/bin/env \
PJARCZAK_BAMBU_PLUGIN_DIR="$PLUGIN_DIR" \
PJARCZAK_BAMBU_NETWORK_SO="$PLUGIN_DIR/libbambu_networking.so" \
PJARCZAK_BAMBU_SOURCE_SO="$PLUGIN_DIR/libBambuSource.so" \
PJARCZAK_BAMBU_LIVE555_SO="$PLUGIN_DIR/liblive555.so" \
PJARCZAK_EXPECTED_BAMBU_NETWORK_VERSION="${PJARCZAK_EXPECTED_BAMBU_NETWORK_VERSION:-}" \
"$HOST_PATH"

View file

@ -0,0 +1,177 @@
#!/bin/bash
set -euo pipefail
PACKAGE_DIR=""
PLUGIN_DIR=""
PLUGIN_CACHE_DIR=""
REPLACE_EXISTING=0
while [[ $# -gt 0 ]]; do
case "$1" in
-PackageDir)
PACKAGE_DIR="${2:-}"
shift 2
;;
-PluginDir)
PLUGIN_DIR="${2:-}"
shift 2
;;
-PluginCacheDir)
PLUGIN_CACHE_DIR="${2:-}"
shift 2
;;
-ReplaceExisting)
REPLACE_EXISTING=1
shift
;;
*)
echo "unknown argument: $1" >&2
exit 2
;;
esac
done
if [[ -z "$PLUGIN_DIR" ]]; then
PLUGIN_DIR="$PACKAGE_DIR"
fi
if [[ -z "$PLUGIN_DIR" ]]; then
echo "PluginDir is required" >&2
exit 2
fi
APP_SUPPORT_DIR="$HOME/Library/Application Support/OrcaSlicer/macos-bridge"
LOCAL_LIMA_ROOT="$APP_SUPPORT_DIR/lima"
LOCAL_LIMA_BIN="$LOCAL_LIMA_ROOT/bin"
mkdir -p "$APP_SUPPORT_DIR" "$LOCAL_LIMA_ROOT"
trim_file() {
local path="$1"
if [[ ! -f "$path" ]]; then
return 1
fi
LC_ALL=C tr -d '\r' < "$path" | head -n 1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
}
find_limactl() {
if [[ -n "${PJARCZAK_LIMACTL:-}" && -x "${PJARCZAK_LIMACTL}" ]]; then
printf '%s\n' "$PJARCZAK_LIMACTL"
return 0
fi
if command -v limactl >/dev/null 2>&1; then
command -v limactl
return 0
fi
if [[ -x "$LOCAL_LIMA_BIN/limactl" ]]; then
printf '%s\n' "$LOCAL_LIMA_BIN/limactl"
return 0
fi
for candidate in /opt/homebrew/bin/limactl /usr/local/bin/limactl; do
if [[ -x "$candidate" ]]; then
printf '%s\n' "$candidate"
return 0
fi
done
return 1
}
install_lima_binary_locally() {
local version
version=$(curl -fsSL https://api.github.com/repos/lima-vm/lima/releases/latest | sed -n 's/.*"tag_name"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
if [[ -z "$version" ]]; then
echo "failed to resolve latest Lima version from GitHub API" >&2
return 1
fi
local host_arch
host_arch=$(uname -m)
case "$host_arch" in
arm64|aarch64)
host_arch=arm64
;;
x86_64|amd64)
host_arch=x86_64
;;
*)
echo "unsupported macOS architecture for Lima: $host_arch" >&2
return 1
;;
esac
local version_no_v="${version#v}"
local base_url="https://github.com/lima-vm/lima/releases/download/${version}"
local main_archive="lima-${version_no_v}-Darwin-${host_arch}.tar.gz"
local guest_archive="lima-additional-guestagents-${version_no_v}-Darwin-${host_arch}.tar.gz"
local tmpdir
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' RETURN
curl -fL "$base_url/$main_archive" -o "$tmpdir/$main_archive"
tar -xzf "$tmpdir/$main_archive" -C "$LOCAL_LIMA_ROOT"
if curl -fL "$base_url/$guest_archive" -o "$tmpdir/$guest_archive"; then
tar -xzf "$tmpdir/$guest_archive" -C "$LOCAL_LIMA_ROOT"
fi
[[ -x "$LOCAL_LIMA_BIN/limactl" ]]
}
ensure_lima_installed() {
LIMACTL=$(find_limactl || true)
if [[ -n "$LIMACTL" ]]; then
return 0
fi
if command -v brew >/dev/null 2>&1; then
brew install lima
LIMACTL=$(find_limactl || true)
if [[ -n "$LIMACTL" ]]; then
return 0
fi
fi
install_lima_binary_locally
LIMACTL=$(find_limactl || true)
[[ -n "$LIMACTL" ]]
}
maybe_install_rosetta() {
if [[ "$(uname -m)" != "arm64" ]]; then
return 0
fi
if pgrep -q oahd >/dev/null 2>&1; then
return 0
fi
/usr/sbin/softwareupdate --install-rosetta --agree-to-license >/dev/null 2>&1 || true
}
INSTANCE="${PJARCZAK_MAC_LIMA_INSTANCE:-}"
if [[ -z "$INSTANCE" ]]; then
INSTANCE=$(trim_file "$PLUGIN_DIR/pjarczak_lima_instance.txt" || true)
fi
if [[ -z "$INSTANCE" ]]; then
INSTANCE="orcaslicer-bambu-network"
fi
ensure_lima_installed
maybe_install_rosetta
START_ARGS=(start "--name=${INSTANCE}" --mount-writable)
MACOS_MAJOR=$(sw_vers -productVersion | awk -F. '{print $1}')
if [[ "$MACOS_MAJOR" -ge 13 ]]; then
START_ARGS+=(--vm-type=vz --network=vzNAT)
if [[ "$(uname -m)" == "arm64" ]]; then
START_ARGS+=(--rosetta)
fi
fi
if [[ "$REPLACE_EXISTING" -eq 1 ]]; then
"$LIMACTL" stop "$INSTANCE" >/dev/null 2>&1 || true
fi
if ! "$LIMACTL" shell "$INSTANCE" -- /usr/bin/env true >/dev/null 2>&1; then
"$LIMACTL" "${START_ARGS[@]}" template:default
fi
"$LIMACTL" start-at-login "$INSTANCE" --enabled >/dev/null 2>&1 || true
"$LIMACTL" shell "$INSTANCE" -- /usr/bin/env true >/dev/null
printf 'runtime installed\n'

View file

@ -0,0 +1 @@
orcaslicer-bambu-network

View file

@ -0,0 +1,116 @@
#!/bin/bash
set -euo pipefail
PACKAGE_DIR=""
PLUGIN_DIR=""
PLUGIN_CACHE_DIR=""
ALLOW_MISSING_LINUX_PLUGIN=0
while [[ $# -gt 0 ]]; do
case "$1" in
-PackageDir)
PACKAGE_DIR="${2:-}"
shift 2
;;
-PluginDir)
PLUGIN_DIR="${2:-}"
shift 2
;;
-PluginCacheDir)
PLUGIN_CACHE_DIR="${2:-}"
shift 2
;;
-AllowMissingLinuxPlugin)
ALLOW_MISSING_LINUX_PLUGIN=1
shift
;;
*)
echo "unknown argument: $1" >&2
exit 2
;;
esac
done
if [[ -z "$PLUGIN_DIR" ]]; then
PLUGIN_DIR="$PACKAGE_DIR"
fi
if [[ -z "$PLUGIN_DIR" ]]; then
echo "PluginDir is required" >&2
exit 2
fi
trim_file() {
local path="$1"
if [[ ! -f "$path" ]]; then
return 1
fi
LC_ALL=C tr -d '\r' < "$path" | head -n 1 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'
}
find_limactl() {
if [[ -n "${PJARCZAK_LIMACTL:-}" && -x "${PJARCZAK_LIMACTL}" ]]; then
printf '%s\n' "$PJARCZAK_LIMACTL"
return 0
fi
if command -v limactl >/dev/null 2>&1; then
command -v limactl
return 0
fi
local local_bin="$HOME/Library/Application Support/OrcaSlicer/macos-bridge/lima/bin/limactl"
if [[ -x "$local_bin" ]]; then
printf '%s\n' "$local_bin"
return 0
fi
for candidate in /opt/homebrew/bin/limactl /usr/local/bin/limactl; do
if [[ -x "$candidate" ]]; then
printf '%s\n' "$candidate"
return 0
fi
done
return 1
}
require_file() {
local path="$1"
local label="$2"
if [[ ! -f "$path" ]]; then
echo "missing required file: $label" >&2
exit 1
fi
}
if [[ "$ALLOW_MISSING_LINUX_PLUGIN" -eq 0 ]]; then
require_file "$PLUGIN_DIR/libbambu_networking.so" "libbambu_networking.so"
require_file "$PLUGIN_DIR/libBambuSource.so" "libBambuSource.so"
fi
require_file "$PLUGIN_DIR/pjarczak_bambu_linux_host" "pjarczak_bambu_linux_host"
require_file "$PLUGIN_DIR/pjarczak_bambu_linux_host_abi1" "pjarczak_bambu_linux_host_abi1"
require_file "$PLUGIN_DIR/pjarczak_bambu_linux_host_abi0" "pjarczak_bambu_linux_host_abi0"
require_file "$PLUGIN_DIR/pjarczak-bambu-linux-host-wrapper" "pjarczak-bambu-linux-host-wrapper"
require_file "$PLUGIN_DIR/install_runtime_macos.sh" "install_runtime_macos.sh"
require_file "$PLUGIN_DIR/verify_runtime_macos.sh" "verify_runtime_macos.sh"
require_file "$PLUGIN_DIR/pjarczak_lima_instance.txt" "pjarczak_lima_instance.txt"
require_file "$PLUGIN_DIR/ca-certificates.crt" "ca-certificates.crt"
require_file "$PLUGIN_DIR/slicer_base64.cer" "slicer_base64.cer"
LIMACTL=$(find_limactl || true)
if [[ -z "$LIMACTL" ]]; then
echo "limactl not found" >&2
exit 1
fi
INSTANCE="${PJARCZAK_MAC_LIMA_INSTANCE:-}"
if [[ -z "$INSTANCE" ]]; then
INSTANCE=$(trim_file "$PLUGIN_DIR/pjarczak_lima_instance.txt" || true)
fi
if [[ -z "$INSTANCE" ]]; then
echo "Lima instance name is not configured" >&2
exit 1
fi
if ! "$LIMACTL" shell "$INSTANCE" -- /usr/bin/env true >/dev/null 2>&1; then
echo "Lima instance '$INSTANCE' is not ready" >&2
exit 1
fi
printf 'runtime ok\n'