mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-01 22:37:03 +00:00
Merge branch 'main' into feature/filament_id
Brings in 560 commits (merge base 2026-07-04 .. origin/main af9fd10d7a).
19 conflicts resolved; the other 138 touched files auto-merged.
Conflict resolutions
- Snapmaker/Polymaker (7): main normalised JSON key order in #15039 while this
branch inserted filament_id/filament_vendor. Took main's key order and kept
this branch's values, inserting a single filament_id key rather than letting
git's line merge leave duplicate "from"/"instantiation" keys.
- re3D rPETG @0.8/@1.75 nozzle (2): main renamed these from "re3D Greengate
rPETG @*" and re-vendored GreenGate3D -> re3D (#15169). Git's rename-aware
merge produced a file with two filament_vendor keys; took main's version
wholesale instead. The id is reconciled by the tooling, not by hand.
- re3D rPP (1): kept main's inherits change (fdm_filament_pet -> fdm_filament_pp,
local filament_type override dropped) and its widened compatible_printers.
The flattened type is still PP, so the product triple and id OFfHGM1D are
unchanged.
- re3D Greengate rPETG.json, Afinia {ABS+,ABS,PLA,TPU,Value ABS,Value PLA}.json
(7 modify/delete): accepted main's deletions. The Afinia ids are not orphaned
- the surviving @HS presets resolve the same triple and already carry the
same ids, so nothing is retired.
- .github/workflows/check_profiles.yml: kept both main's new "validate slice"
step and this branch's tree-wide filament-subtype check.
- tests/libslic3r/CMakeLists.txt: kept both test_filament_id_succession.cpp and
main's test_preset_diff.cpp.
Verified
- No conflict markers; all 12795 profile JSONs parse with no duplicate keys.
- All branch artifacts (tooling, snapshot, ledger, doc, tests) intact and
unmodified by the merge.
- The succession-ledger C++ call sites survived main's refactors; audited
Preset.{cpp,hpp}, PresetBundle.cpp, DeviceManager.cpp, PresetComboBoxes.cpp,
CaliHistoryDialog.cpp, MoonrakerPrinterAgent.cpp against base/ours/theirs.
- scripts/tests: 115/116 pass.
Known follow-up: assign_filament_ids.py --check reports 243 errors, all from
filament data main added in the last month (BBL/addnorth, Qidi X5/Plus 5,
Snapmaker U1, re3D). The single failing unit test is the live-tree conformance
test asserting that count is zero. Reconciliation lands separately.
This commit is contained in:
@@ -161,6 +161,12 @@ modules:
|
||||
- /include
|
||||
- "*.a"
|
||||
- "*.la"
|
||||
# CPython headers: useless at runtime (no compiler in the runtime
|
||||
# sandbox, so C-extension source builds can't happen anyway), and a
|
||||
# few MB of bloat. /app/libpython is installed by this module, so the
|
||||
# entry must live in THIS module's cleanup (module-level cleanup only
|
||||
# matches files the module itself installed).
|
||||
- /libpython/include
|
||||
|
||||
sources:
|
||||
# OrcaSlicer deps/ directory (avoids copying .git from worktree)
|
||||
@@ -294,6 +300,18 @@ modules:
|
||||
sha256: 1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277
|
||||
dest: external-packages/OpenCV
|
||||
|
||||
# CPython 3.12.13
|
||||
- type: file
|
||||
url: https://www.python.org/ftp/python/3.12.13/Python-3.12.13.tar.xz
|
||||
sha256: c08bc65a81971c1dd5783182826503369466c7e67374d1646519adf05207b684
|
||||
dest: external-packages/python3
|
||||
|
||||
# wxInspector 1.0.0
|
||||
- type: file
|
||||
url: https://github.com/Noisyfox/wxInspector/archive/refs/tags/v1.0.0.zip
|
||||
sha256: 0ba163956f2d468b19a91b96c5aba66ee9610843ea41dda628ea44cdafde7db7
|
||||
dest: external-packages/wxInspector
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Fallback archives for deps normally provided by the GNOME SDK.
|
||||
# These are only used if find_package() fails to locate them.
|
||||
|
||||
212
scripts/generate_orca_python_stubs.py
Normal file
212
scripts/generate_orca_python_stubs.py
Normal file
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate Python .pyi stubs for OrcaSlicer's pybind11 plugin API.
|
||||
|
||||
The script creates a local virtual environment, installs pybind11-stubgen,
|
||||
imports the built `orca` extension module, and writes stubs to ./typings by
|
||||
default. It intentionally does not update editor settings.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import sysconfig
|
||||
import venv
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
DEFAULT_VENV = REPO_ROOT / ".venv-stubgen"
|
||||
DEFAULT_OUTPUT = REPO_ROOT / "typings"
|
||||
DEFAULT_BUILD_DIR = REPO_ROOT / "build"
|
||||
DEFAULT_CONFIG = "RelWithDebInfo"
|
||||
MODULE_NAME = "orca"
|
||||
|
||||
|
||||
def log(message: str) -> None:
|
||||
print(f"[orca-stubgen] {message}")
|
||||
|
||||
|
||||
def run(command: list[str], *, env: dict[str, str] | None = None, cwd: Path = REPO_ROOT) -> None:
|
||||
log(" ".join(command))
|
||||
subprocess.run(command, cwd=cwd, env=env, check=True)
|
||||
|
||||
|
||||
def venv_python(venv_dir: Path) -> Path:
|
||||
if os.name == "nt":
|
||||
return venv_dir / "Scripts" / "python.exe"
|
||||
return venv_dir / "bin" / "python"
|
||||
|
||||
|
||||
def ensure_venv(venv_dir: Path) -> Path:
|
||||
python = venv_python(venv_dir)
|
||||
if not python.exists():
|
||||
log(f"creating virtual environment: {venv_dir}")
|
||||
venv.EnvBuilder(with_pip=True).create(venv_dir)
|
||||
return python
|
||||
|
||||
|
||||
def ensure_stubgen(python: Path) -> None:
|
||||
probe = subprocess.run(
|
||||
[str(python), "-m", "pybind11_stubgen", "--help"],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
)
|
||||
if probe.returncode == 0:
|
||||
return
|
||||
|
||||
run([str(python), "-m", "pip", "install", "--upgrade", "pip"])
|
||||
run([str(python), "-m", "pip", "install", "pybind11-stubgen"])
|
||||
|
||||
|
||||
def module_suffixes() -> list[str]:
|
||||
suffixes = [".pyd", ".so", ".dylib"]
|
||||
ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
|
||||
if ext_suffix:
|
||||
suffixes.insert(0, str(ext_suffix))
|
||||
return list(dict.fromkeys(suffixes))
|
||||
|
||||
|
||||
def is_orca_extension(path: Path) -> bool:
|
||||
if not path.is_file():
|
||||
return False
|
||||
if path.name == f"{MODULE_NAME}.so" or path.name == f"{MODULE_NAME}.pyd":
|
||||
return True
|
||||
return any(path.name.startswith(f"{MODULE_NAME}.") and path.name.endswith(suffix) for suffix in module_suffixes())
|
||||
|
||||
|
||||
def find_module_dir(build_dir: Path, config: str) -> Path | None:
|
||||
candidates = [
|
||||
build_dir / "src" / "slic3r" / config,
|
||||
build_dir / "src" / "slic3r",
|
||||
REPO_ROOT / "build" / "src" / "slic3r" / config,
|
||||
REPO_ROOT / "build" / "src" / "slic3r",
|
||||
REPO_ROOT / "build" / "arm64" / "src" / "slic3r" / config,
|
||||
REPO_ROOT / "build" / "arm64" / "src" / "slic3r",
|
||||
]
|
||||
|
||||
for candidate in candidates:
|
||||
if not candidate.exists():
|
||||
continue
|
||||
if any(is_orca_extension(path) for path in candidate.iterdir()):
|
||||
return candidate
|
||||
|
||||
for root in dict.fromkeys([build_dir, REPO_ROOT / "build", REPO_ROOT / "build" / "arm64"]):
|
||||
if not root.exists():
|
||||
continue
|
||||
for path in root.rglob(f"{MODULE_NAME}*"):
|
||||
if is_orca_extension(path):
|
||||
return path.parent
|
||||
return None
|
||||
|
||||
|
||||
def build_stubgen_module(build_dir: Path, config: str) -> None:
|
||||
run([
|
||||
"cmake",
|
||||
"--build",
|
||||
str(build_dir),
|
||||
"--config",
|
||||
config,
|
||||
"--target",
|
||||
"orca_stubgen",
|
||||
"--",
|
||||
])
|
||||
|
||||
|
||||
def import_env(module_dir: Path) -> dict[str, str]:
|
||||
env = os.environ.copy()
|
||||
existing = env.get("PYTHONPATH")
|
||||
paths = [str(module_dir)]
|
||||
if existing:
|
||||
paths.append(existing)
|
||||
env["PYTHONPATH"] = os.pathsep.join(paths)
|
||||
return env
|
||||
|
||||
|
||||
def verify_import(python: Path, module_dir: Path) -> None:
|
||||
code = (
|
||||
"import orca; "
|
||||
"print(orca.__file__); "
|
||||
"assert hasattr(orca, 'printer_agent'), 'orca.printer_agent is missing'"
|
||||
)
|
||||
run([str(python), "-c", code], env=import_env(module_dir))
|
||||
|
||||
|
||||
def clean_output(output_dir: Path) -> None:
|
||||
package_dir = output_dir / MODULE_NAME
|
||||
module_stub = output_dir / f"{MODULE_NAME}.pyi"
|
||||
if package_dir.exists():
|
||||
shutil.rmtree(package_dir)
|
||||
if module_stub.exists():
|
||||
module_stub.unlink()
|
||||
|
||||
|
||||
def generate_stubs(python: Path, module_dir: Path, output_dir: Path, ignore_errors: bool) -> None:
|
||||
command = [str(python), "-m", "pybind11_stubgen", MODULE_NAME, "-o", str(output_dir)]
|
||||
if ignore_errors:
|
||||
command.append("--ignore-all-errors")
|
||||
run(command, env=import_env(module_dir))
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--venv", type=Path, default=DEFAULT_VENV, help="Virtual environment path.")
|
||||
parser.add_argument("--output", type=Path, default=DEFAULT_OUTPUT, help="Directory for generated stubs.")
|
||||
parser.add_argument("--build-dir", type=Path, default=DEFAULT_BUILD_DIR, help="CMake build directory.")
|
||||
parser.add_argument("--config", default=DEFAULT_CONFIG, help="CMake configuration name.")
|
||||
parser.add_argument("--module-dir", type=Path, help="Directory containing the built orca extension module.")
|
||||
parser.add_argument("--build-missing", action="store_true", help="Build the orca_stubgen target if orca is missing.")
|
||||
parser.add_argument("--clean", action="store_true", help="Remove existing generated orca stubs before writing new ones.")
|
||||
parser.add_argument(
|
||||
"--strict",
|
||||
action="store_true",
|
||||
help="Do not pass --ignore-all-errors to pybind11-stubgen.",
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
venv_dir = args.venv.resolve()
|
||||
output_dir = args.output.resolve()
|
||||
build_dir = args.build_dir.resolve()
|
||||
module_dir = args.module_dir.resolve() if args.module_dir else None
|
||||
|
||||
python = ensure_venv(venv_dir)
|
||||
ensure_stubgen(python)
|
||||
|
||||
if module_dir is None:
|
||||
module_dir = find_module_dir(build_dir, args.config)
|
||||
|
||||
if module_dir is None and args.build_missing:
|
||||
build_stubgen_module(build_dir, args.config)
|
||||
module_dir = find_module_dir(build_dir, args.config)
|
||||
|
||||
if module_dir is None:
|
||||
print(
|
||||
"Could not find a built orca extension module.\n"
|
||||
f"Expected something like: {build_dir / 'src' / 'slic3r' / args.config / 'orca.so'}\n"
|
||||
"Build it with:\n"
|
||||
f" cmake --build {build_dir} --config {args.config} --target orca_stubgen --\n"
|
||||
"Or rerun this script with --build-missing.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
if args.clean:
|
||||
clean_output(output_dir)
|
||||
|
||||
log(f"using module directory: {module_dir}")
|
||||
verify_import(python, module_dir)
|
||||
generate_stubs(python, module_dir, output_dir, ignore_errors=not args.strict)
|
||||
log(f"wrote stubs to: {output_dir}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -2,11 +2,13 @@
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:rescap3="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities/3"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:desktop6="http://schemas.microsoft.com/appx/manifest/desktop/windows10/6"
|
||||
xmlns:virtualization="http://schemas.microsoft.com/appx/manifest/virtualization/windows10"
|
||||
IgnorableNamespaces="uap rescap rescap3 desktop6 virtualization">
|
||||
IgnorableNamespaces="uap uap3 rescap rescap3 desktop desktop6 virtualization">
|
||||
|
||||
<Identity Name="@MSIX_IDENTITY_NAME@"
|
||||
Publisher="@MSIX_PUBLISHER@"
|
||||
@@ -64,6 +66,20 @@
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="orcaslicer" />
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="prusaslicer" />
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="bambustudio" />
|
||||
</uap:Extension>
|
||||
<uap:Extension Category="windows.protocol">
|
||||
<uap:Protocol Name="cura" />
|
||||
</uap:Extension>
|
||||
<uap3:Extension Category="windows.appExecutionAlias" EntryPoint="Windows.FullTrustApplication">
|
||||
<uap3:AppExecutionAlias>
|
||||
<desktop:ExecutionAlias Alias="orca-slicer.exe" />
|
||||
</uap3:AppExecutionAlias>
|
||||
</uap3:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
@@ -47,12 +47,16 @@ def no_duplicates_object_pairs_hook(pairs):
|
||||
return seen
|
||||
|
||||
# NOTE: currently Orca expects compatible_printers to be a defined in every instantiation profile, inheritation is not supported in Profile page
|
||||
def check_filament_compatible_printers(vendor_folder):
|
||||
def check_filament_compatible_printers(vendor, vendor_folder):
|
||||
"""
|
||||
Checks JSON files in the vendor folder for missing or empty 'compatible_printers'
|
||||
when 'instantiation' is flagged as true.
|
||||
|
||||
In the OrcaFilamentLibrary 'compatible_printers' is optional: a profile without it is generic and
|
||||
offered on every printer, while a profile that lists printers supersedes the generic one there.
|
||||
|
||||
Parameters:
|
||||
vendor (str): The vendor name the folder belongs to.
|
||||
vendor_folder (str or Path): The directory to search for JSON profile files.
|
||||
|
||||
Returns:
|
||||
@@ -116,7 +120,7 @@ def check_filament_compatible_printers(vendor_folder):
|
||||
|
||||
for profile in profiles.values():
|
||||
instantiation = str(profile['content'].get("instantiation", "")).lower() == "true"
|
||||
if instantiation:
|
||||
if instantiation and vendor != 'OrcaFilamentLibrary':
|
||||
try:
|
||||
compatible_printers = get_property(profile, "compatible_printers")
|
||||
if not compatible_printers or (isinstance(compatible_printers, list) and not compatible_printers):
|
||||
@@ -578,7 +582,7 @@ def main():
|
||||
vendor_path = profiles_dir / vendor_name
|
||||
|
||||
if args.check_filaments or not (args.check_materials and not args.check_filaments):
|
||||
errors_found += check_filament_compatible_printers(vendor_path / "filament")
|
||||
errors_found += check_filament_compatible_printers(vendor_name, vendor_path / "filament")
|
||||
|
||||
if args.check_materials:
|
||||
new_errors, new_warnings = check_machine_default_materials(profiles_dir, vendor_name)
|
||||
|
||||
@@ -23,7 +23,7 @@ if %FULL_MODE%==1 (
|
||||
call :prepareGettextList "%list_file%" "%filtered_list%" "%missing_list%"
|
||||
if "!has_sources!"=="1" (
|
||||
if not exist "%generated_i18n%" mkdir "%generated_i18n%"
|
||||
.\tools\xgettext.exe --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "%filtered_list%" -o "%generated_pot%"
|
||||
.\tools\xgettext.exe --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_CONTEXT:1,2c --keyword=_u8L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "%filtered_list%" -o "%generated_pot%"
|
||||
if errorlevel 1 (
|
||||
set "script_exit_code=1"
|
||||
) else (
|
||||
|
||||
@@ -85,7 +85,7 @@ if $FULL_MODE; then
|
||||
generated_pot_file="${generated_i18n_dir}/OrcaSlicer.pot"
|
||||
|
||||
mkdir -p "$generated_i18n_dir"
|
||||
xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "$filtered_list" -o "$generated_pot_file"
|
||||
xgettext --keyword=L --keyword=_L --keyword=_u8L --keyword=L_CONTEXT:1,2c --keyword=_L_CONTEXT:1,2c --keyword=_u8L_CONTEXT:1,2c --keyword=_L_PLURAL:1,2 --add-comments=TRN --from-code=UTF-8 --no-location --debug --boost --no-wrap -f "$filtered_list" -o "$generated_pot_file"
|
||||
python3 scripts/HintsToPot.py ./resources "$generated_i18n_dir"
|
||||
|
||||
if [ -f "$pot_file" ] && files_equal_ignoring_pot_date "$pot_file" "$generated_pot_file"; then
|
||||
|
||||
@@ -4,11 +4,21 @@
|
||||
# It should only require the directories build/tests, scripts/, and tests/ to function,
|
||||
# and cmake (with ctest) installed.
|
||||
# (otherwise, update the workflow too, but try to avoid to keep things self-contained)
|
||||
#
|
||||
# Usage: run_unit_tests.sh [TEST_DIR] [BUILD_CONFIG]
|
||||
# TEST_DIR directory containing the built tests (default: build/tests)
|
||||
# BUILD_CONFIG configuration to run; required for multi-config generators
|
||||
# (Windows/macOS), harmless/omitted for single-config (Linux).
|
||||
|
||||
ROOT_DIR="$(dirname "$0")/.."
|
||||
|
||||
cd "${ROOT_DIR}" || exit 1
|
||||
|
||||
TEST_DIR="${1:-build/tests}"
|
||||
BUILD_CONFIG="${2:-}"
|
||||
|
||||
# Run the whole suite, excluding tests tagged [NotWorking].
|
||||
# --no-tests=error fails the job if the filter matches nothing (instead of passing green).
|
||||
ctest --test-dir build/tests -LE "NotWorking" --no-tests=error --output-junit "$(pwd)/ctest_results.xml" --output-on-failure -j
|
||||
args=(--test-dir "${TEST_DIR}" -LE "NotWorking" --no-tests=error --output-junit "$(pwd)/ctest_results.xml" --output-on-failure -j)
|
||||
[ -n "${BUILD_CONFIG}" ] && args+=(--build-config "${BUILD_CONFIG}")
|
||||
ctest "${args[@]}"
|
||||
|
||||
Reference in New Issue
Block a user