mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 14:32:36 +00:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
daa75b3fa0 | ||
|
|
42b506ad85 | ||
|
|
84c7186674 | ||
|
|
9670da7ddd | ||
|
|
c858360ee6 | ||
|
|
1ecedd3187 | ||
|
|
0346c4931a | ||
|
|
227bdb77da | ||
|
|
2a8782b8f6 | ||
|
|
c0dfe50bc5 | ||
|
|
0e0e34c8b4 |
@@ -1,86 +0,0 @@
|
|||||||
# DELEGATION SPECIFICATION: HARNESS-DRIVEN VALIDATION LOOP
|
|
||||||
slug: sketch-focus-arbiter · repo: /home/tommaso/projects/apps/orca_cad · branch: cad-mainline
|
|
||||||
|
|
||||||
## 1. TARGET GOAL
|
|
||||||
|
|
||||||
**Functional Objective.** Keyboard input in the Design tab is routed by WHAT THE KEY IS, not by
|
|
||||||
which widget the window manager decided to focus. Adopted from FreeCAD's
|
|
||||||
`DrawSketchKeyboardManager::detectKeyboardEventHandlingMode`
|
|
||||||
(src/Mod/Sketcher/Gui/DrawSketchKeyboardManager.cpp), which never queries focus at all:
|
|
||||||
|
|
||||||
- digit, `-`, `.`, `,` -> the open value field
|
|
||||||
- Backspace / Delete -> the open value field (when one is open)
|
|
||||||
- Enter / Return / Tab -> commit the field, control returns to the view
|
|
||||||
- a letter -> the sketch-tool shortcut map, as today
|
|
||||||
- Esc -> the existing CadLevel LIFO (DesignInteraction.hpp), unchanged
|
|
||||||
- anything else -> sticky: whoever had it keeps it
|
|
||||||
|
|
||||||
Observable postcondition: for EVERY sketch tool that opens a value field, a value typed
|
|
||||||
immediately after the field appears — with NO click into the field — is the value committed.
|
|
||||||
Today the prefill is committed instead whenever the WM withholds focus.
|
|
||||||
|
|
||||||
**Target Files / Scope (writable).**
|
|
||||||
src/slic3r/GUI/CAD/DesignPanel.cpp (the arbiter lives in the existing wxEVT_CHAR_HOOK)
|
|
||||||
src/slic3r/GUI/CAD/DesignCanvas.cpp/.hpp (forwarding entry points only)
|
|
||||||
src/slic3r/GUI/CAD/SketchInlineEditor.cpp/.hpp (accept a programmatically delivered character)
|
|
||||||
scripts/CAD/check-gui-click-edit.py (F2P oracle — authoring exception, see §4)
|
|
||||||
Everything else read-only. No dependency additions, no reformatting.
|
|
||||||
|
|
||||||
**Open Bindings.**
|
|
||||||
- The in-canvas ImGui field on wip/in-canvas-value-field is NOT in scope. Default: the arbiter
|
|
||||||
is implemented against the CURRENT wxFrame field on cad-mainline, because content-based
|
|
||||||
routing makes the window's focus irrelevant either way. If it later moves in-canvas the
|
|
||||||
arbiter is unchanged.
|
|
||||||
- Tools whose field is opened by a toolbar button rather than a gesture (Constrain path) are
|
|
||||||
covered by the same arbiter but are not in the F2P tool list. Default: assert them in P2P only.
|
|
||||||
|
|
||||||
## 2. HARNESS ENVIRONMENT & GROUND TRUTH
|
|
||||||
|
|
||||||
The rig container `orcacad-gui` on nativedev IS the harness. Xvfb `:11` + openbox, the app under
|
|
||||||
test, `xdotool` for synthetic input, and an MCP socket at `/tmp/mcp.sock` that reports sketch
|
|
||||||
state as JSON. It is a closed loop: drive input, read geometry back, assert. No window manager
|
|
||||||
politics, no human.
|
|
||||||
|
|
||||||
Harness interface (ordered; each slot one invocation, one exit code):
|
|
||||||
S1 sync docker cp <file> orcacad-gui:/OrcaSlicer/<path>
|
|
||||||
S2 build docker exec orcacad-gui ninja -C /OrcaSlicer/build orca-slicer
|
|
||||||
S3 restart docker exec orcacad-gui /OrcaSlicer/scripts/CAD/start-headless-gui.sh
|
|
||||||
S4 F2P docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-click-edit.py --attach
|
|
||||||
S5 P2P docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-sketching.py
|
|
||||||
|
|
||||||
**F2P.** `scripts/CAD/check-gui-click-edit.py`. For each of Line, Rectangle, Circle, Slot,
|
|
||||||
Polygon, Ellipse and Rounded rectangle: arm the tool, draw it, and type a value that differs
|
|
||||||
from the prefill WITHOUT clicking the field. Assert the committed value equals the typed value.
|
|
||||||
The ladder must FAIL against unmodified cad-mainline — that is what proves it asserts something.
|
|
||||||
|
|
||||||
**P2P.** `scripts/CAD/check-gui-sketching.py`, the existing gesture ladder, minus anything red at
|
|
||||||
baseline. NOTE: it calls `focus_field()` — one click into the field before typing — which is the
|
|
||||||
workaround this whole task removes. It stays green as a regression guard; it is NOT evidence.
|
|
||||||
|
|
||||||
**Test Integrity Constraint.** `focus_field()` in check-gui-sketching.py must NOT be deleted to
|
|
||||||
make things pass, and check-gui-click-edit.py must NOT be weakened. Either invalidates the run.
|
|
||||||
|
|
||||||
## 3. VERIFICATION COMMANDS
|
|
||||||
1. Static: `docker exec orcacad-gui ninja -C /OrcaSlicer/build orca-slicer` (warnings delta only;
|
|
||||||
this repo configures no linter — the compiler is the static gate. Absolute-zero is NOT the gate.)
|
|
||||||
2. Harness: `docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-click-edit.py --attach`
|
|
||||||
3. Regression: `docker exec -e DISPLAY=:11 orcacad-gui python3 /tmp/check-gui-sketching.py`
|
|
||||||
|
|
||||||
## 4. CONVERGENCE LOOP — ceiling 8 iterations
|
|
||||||
EDIT (scoped) -> EXECUTE S1..S5 -> PARSE the ladder's per-tool assertions and the [UX]/[KEYTRACE]
|
|
||||||
lines -> PATCH from the parsed cause. On ceiling without convergence: stop, report the last diff
|
|
||||||
and the unresolved failure set. Do not report success.
|
|
||||||
|
|
||||||
F2P authoring exception: check-gui-click-edit.py is writable, and must be shown RED against
|
|
||||||
unmodified source before any source edit counts.
|
|
||||||
|
|
||||||
## 5. TERMINATION CRITERIA
|
|
||||||
- [ ] S2 exits 0, and introduces no compiler warning absent from the baseline.
|
|
||||||
- [ ] S4 ALL_PASSED — every tool commits the typed value, no click into the field.
|
|
||||||
- [ ] S5 shows zero regressions against its recorded baseline pass count.
|
|
||||||
- [ ] F2P proven red without the fix (source stashed, ladder re-run, must FAIL).
|
|
||||||
|
|
||||||
## 6. GUARDRAILS
|
|
||||||
Zero-assumption: no completion claim without captured stdout and exit codes. Oracle supremacy:
|
|
||||||
the ladder's verdict overrides my judgement. Blast radius: §1 files only. Baseline obligation:
|
|
||||||
run §3 once before the first edit and record it.
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
---
|
|
||||||
name: orca-profiles
|
|
||||||
description: Use when creating, modifying, reviewing or debugging OrcaSlicer FFF system profiles under resources/profiles, including printer/vendor/nozzle/material additions, bundle indexes and versions, preset renames, setting_id and filament_id. Also use for missing presets or vendors, ignored profile settings, ambiguous AMS filament matches, and failures from orca_profile_tool.py, check_profile.sh/.bat, OrcaSlicer_profile_validator or the Check profiles CI job.
|
|
||||||
---
|
|
||||||
|
|
||||||
# OrcaSlicer system profiles
|
|
||||||
|
|
||||||
A bundle is `resources/profiles/<Vendor>.json` plus `<Vendor>/`. The vendor id is the
|
|
||||||
filename stem, not the index's display `name`. The index is the loader's only entry point:
|
|
||||||
unindexed presets never load. `OrcaFilamentLibrary` is the shared filament bundle;
|
|
||||||
`blacklist.json` is data, not a bundle.
|
|
||||||
|
|
||||||
## Choose the reference for the task
|
|
||||||
|
|
||||||
Read the relevant reference before editing; load others only when the task crosses those areas.
|
|
||||||
Paths below are relative to this skill. Commands run from the repository root.
|
|
||||||
|
|
||||||
| Task | Read |
|
|
||||||
| --- | --- |
|
|
||||||
| Add or tune a filament, brand or material; fix compatibility / alias shadowing | [filament-profiles.md](references/filament-profiles.md) |
|
|
||||||
| Add a printer or nozzle; change models, variants, assets or extruder vectors | [machine-profiles.md](references/machine-profiles.md) |
|
|
||||||
| Add a quality tier or tune a process | [process-profiles.md](references/process-profiles.md) |
|
|
||||||
| Create a vendor bundle; diagnose loading or inheritance; migrate preset names | [vendor-bundle.md](references/vendor-bundle.md) |
|
|
||||||
| Change ids; diagnose AMS identity | [ids.md](references/ids.md), then `docs/HLSD/filament_id.md` for identity changes |
|
|
||||||
| Review a profile diff | [review-checklist.md](references/review-checklist.md) |
|
|
||||||
| Run checks, interpret failures, test another tree or verify in the app | [validation.md](references/validation.md) |
|
|
||||||
|
|
||||||
## Golden rules
|
|
||||||
|
|
||||||
1. **Bump every changed bundle's `version`**, including `OrcaFilamentLibrary.json` when affected.
|
|
||||||
Increment the last component; carry `.99` into the third component (`02.04.00.99` →
|
|
||||||
`02.04.01.00`). The updater requires a strictly newer version. CI does not check this.
|
|
||||||
2. **Register every preset, bases included, parents before children.** `update-index` generates
|
|
||||||
the four `*_list` arrays; `check` requires its output. Index names must equal file `name` fields.
|
|
||||||
3. **Generate ids; never invent or copy them.** Keep existing ids during ordinary tuning. New
|
|
||||||
presets normally omit them until `generate-id`; bases must have no `setting_id`.
|
|
||||||
BBL's authoritative `setting_id` and a wrongly inherited `filament_id` need the explicit
|
|
||||||
handling in [ids.md](references/ids.md).
|
|
||||||
4. **Load failures can discard a whole vendor bundle.** Broken `inherits`, missing indexed files,
|
|
||||||
duplicate names, invalid model/variant references and unresolved filament ids affect more than
|
|
||||||
the edited preset. Inheritance stays within a bundle, except filaments may inherit the library.
|
|
||||||
5. **Preserve shipped selectable names.** Renaming, deleting or changing `instantiation` from
|
|
||||||
`"true"` to `"false"` needs `renamed_from` on a selectable successor. It is a `;`-separated string;
|
|
||||||
update in-tree references too. See [migration rules](references/vendor-bundle.md#renamed_from).
|
|
||||||
6. **Compatibility uses exact printer variant names.** Every instantiated non-library filament
|
|
||||||
needs a non-empty `compatible_printers` in its own file. Library fallbacks may omit it;
|
|
||||||
library printer-specific tunes use a non-empty list. Keep same-product tunes disjoint.
|
|
||||||
7. **Preset values are strings or arrays of strings.** Use `"instantiation": "false"`, not `false`.
|
|
||||||
Model `nozzle_diameter` is a `;`-separated string; machine `nozzle_diameter` is an array.
|
|
||||||
Wrong types can abort loading; see [failure scopes](references/vendor-bundle.md#failure-modes-ranked-by-blast-radius).
|
|
||||||
8. **Verify setting keys against the code.** Unknown keys are silently discarded. Check
|
|
||||||
`PrintConfig.cpp` definitions and `PrintConfigDef::handle_legacy`; neighbours can contain dead
|
|
||||||
keys. `normalize` removes known obsolete keys, but does not detect arbitrary misspellings.
|
|
||||||
9. **Run the full profile checks before reporting completion.** A vendor-scoped pass is only a
|
|
||||||
development loop. Review also covers version bumps, assets, non-default processes and hardware
|
|
||||||
tuning that CI cannot establish.
|
|
||||||
|
|
||||||
## Creating or modifying a profile
|
|
||||||
|
|
||||||
1. **Inspect the diff and neighbouring presets.** Read their `name`, parent chain and children;
|
|
||||||
edits to a base or a leaf with descendants propagate. Match the bundle's structure and write
|
|
||||||
only overrides. New files use tab indentation, LF and a trailing newline; preserve unrelated
|
|
||||||
formatting in existing files. Match filename case exactly and use cross-platform names.
|
|
||||||
2. **Author explicit metadata.** Set `type` yourself, especially for `machine` vs `machine_model`.
|
|
||||||
Use `"from": "system"` and string `instantiation` on config presets. Omit ids on new presets
|
|
||||||
unless [ids.md](references/ids.md) requires special handling; retain them on existing ones.
|
|
||||||
Complete compatibility, defaults, assets and any rename migration using the task reference.
|
|
||||||
3. **Bump the version**, then run the authoring commands in order for each affected bundle:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 scripts/orca_profile_tool.py normalize --vendor "<Vendor>"
|
|
||||||
python3 scripts/orca_profile_tool.py update-index --vendor "<Vendor>"
|
|
||||||
python3 scripts/orca_profile_tool.py generate-id --vendor "<Vendor>"
|
|
||||||
python3 scripts/orca_profile_tool.py check
|
|
||||||
```
|
|
||||||
|
|
||||||
Writing commands support `--dry-run`. Inspect their diffs: `normalize` changes content and can
|
|
||||||
reformat entire files. Stop and resolve command errors before proceeding.
|
|
||||||
|
|
||||||
**Do not use `trim` in this workflow:** it can delete newly authored, unindexed profiles.
|
|
||||||
Do not use `normalize --force` for routine edits.
|
|
||||||
4. **Validate:**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./scripts/check_profile.sh --vendor "<Vendor>" # development loop
|
|
||||||
./scripts/check_profile.sh # full tree before the PR
|
|
||||||
```
|
|
||||||
|
|
||||||
On Windows use `py -3` instead of `python3`, and `scripts\check_profile.bat -Vendor "<Vendor>"`
|
|
||||||
/ `scripts\check_profile.bat`. Logs: `.test/check_profiles/logs/<check>.log`.
|
|
||||||
Id checks remain tree-wide under `--vendor`; filament-only bundles skip the default slice check.
|
|
||||||
See [validation.md](references/validation.md) for flags, coverage and error remedies.
|
|
||||||
5. **Verify the changed behavior.** Slice newly added non-default processes explicitly, and
|
|
||||||
[test in the app](references/validation.md#testing-in-the-app) for selection or UI behavior.
|
|
||||||
Report checks actually run, failures/skips and any hardware tuning still unverified.
|
|
||||||
|
|
||||||
## Symptom → first reference
|
|
||||||
|
|
||||||
| Symptom | Start here |
|
|
||||||
| --- | --- |
|
|
||||||
| A vendor disappears | Loader log / `validate_system`; [bundle failure scopes](references/vendor-bundle.md#failure-modes-ranked-by-blast-radius) |
|
|
||||||
| A setting has no effect | Key spelling/type, `handle_legacy`, or a config key placed on a `machine_model` |
|
|
||||||
| A preset exists but is not selectable | Index registration, `instantiation`, installation and compatibility |
|
|
||||||
| A filament is missing, duplicated, or matches the wrong spool | [Compatibility and alias shadowing](references/filament-profiles.md#compatible_printers); [ids](references/ids.md) |
|
|
||||||
| A bed temperature is ignored | [Plate-specific temperature keys](references/filament-profiles.md#bed-temperature-is-twelve-keys-not-one) |
|
|
||||||
| A change is absent from the running app | Version bump and [installed profile location](references/validation.md#testing-in-the-app) |
|
|
||||||
| A check fails | [Error → remedy](references/validation.md#error--remedy) |
|
|
||||||
|
|
||||||
## Source of truth
|
|
||||||
|
|
||||||
When guidance and behavior disagree, inspect the current checkout:
|
|
||||||
`scripts/orca_profile_tool.py` for tooling and flags; `src/libslic3r/Preset*.cpp` for loading and
|
|
||||||
compatibility; `src/libslic3r/PrintConfig.cpp` for setting types and legacy handling;
|
|
||||||
`src/dev-utils/OrcaSlicer_profile_validator.cpp` and `.github/workflows/check_profiles.yml` for
|
|
||||||
validation coverage. `docs/HLSD/filament_id.md` defines filament identity. The
|
|
||||||
[profile development guide](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/main/developer_reference/how_to_create_profiles.md)
|
|
||||||
is a tutorial; confirm loader and CLI details against these sources.
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
# Filament profiles and OrcaFilamentLibrary
|
|
||||||
|
|
||||||
`OrcaFilamentLibrary` is the filament-only bundle the loader reads **first**; its config map
|
|
||||||
becomes the base bundle, so any vendor may inherit a library preset by name. It is the only cross-bundle
|
|
||||||
parent — vendor-to-vendor inheritance always fails.
|
|
||||||
|
|
||||||
## Where a filament goes
|
|
||||||
|
|
||||||
| Contribution | Location |
|
|
||||||
| --- | --- |
|
|
||||||
| Generic material for all printers | `OrcaFilamentLibrary/filament/Generic <mat> @System.json` |
|
|
||||||
| A brand's product, all printers | `OrcaFilamentLibrary/filament/<Brand>/` |
|
|
||||||
| A brand's tune for one printer | `OrcaFilamentLibrary/filament/<Brand>/<PrinterVendor>/` — recommended; `<PrinterVendor>/filament/<Brand>/` also works |
|
|
||||||
| A printer vendor's tune of a generic or its own product | `<Vendor>/filament/` |
|
|
||||||
|
|
||||||
Both locations for the last-but-one row are supported: `OrcaFilamentLibrary/filament/<Brand>/<PrinterVendor>/<Name>.json`
|
|
||||||
(the shape the wiki shows) and `<PrinterVendor>/filament/<Brand>/`. The library path is the one a
|
|
||||||
filament vendor should contribute to — `OrcaFilamentLibrary/filament/<Brand>/` is the brand's own
|
|
||||||
folder, while a printer vendor's folder belongs to that printer vendor. Brand tunes do ship under
|
|
||||||
printer vendors' folders today (Polymaker and SUNLU among others).
|
|
||||||
|
|
||||||
Library layout: `filament/base/fdm_filament_*.json` type roots, root-level `Generic <mat> @System.json`
|
|
||||||
generics, and one subfolder per brand, which may nest printer-specific tunes one level deeper. Adding
|
|
||||||
a brand means adding a folder here; the folder name is a directory label only — `filament_vendor` inside the JSON is the real vendor string.
|
|
||||||
|
|
||||||
## The three-part shape
|
|
||||||
|
|
||||||
```jsonc
|
|
||||||
// Fiberon PA6-CF @base.json — the product root, holds identity + material values
|
|
||||||
{ "type": "filament", "name": "Fiberon PA6-CF @base", "from": "system",
|
|
||||||
"instantiation": "false", "inherits": "fdm_filament_pa",
|
|
||||||
"filament_id": "OFkOviHk", // generated here; variants inherit it
|
|
||||||
"filament_vendor": ["Polymaker"], "filament_type": ["PA6-CF"], /* … */ }
|
|
||||||
|
|
||||||
// Fiberon PA6-CF @System.json — the selectable shim, 7 keys
|
|
||||||
{ "type": "filament", "name": "Fiberon PA6-CF @System", "from": "system",
|
|
||||||
"instantiation": "true", "inherits": "Fiberon PA6-CF @base",
|
|
||||||
"setting_id": "…", "compatible_printers": [] }
|
|
||||||
|
|
||||||
// <PrinterVendor>/filament/Polymaker/Fiberon PA6-CF @BBL X1C.json — a printer tune
|
|
||||||
{ … "inherits": "Fiberon PA6-CF @base", "filament_max_volumetric_speed": ["14"],
|
|
||||||
"compatible_printers": ["Bambu Lab X1 Carbon 0.4 nozzle", …] }
|
|
||||||
```
|
|
||||||
|
|
||||||
- `@base` is the convention for a root. A base carries **no** `setting_id`, no `compatible_printers`, no
|
|
||||||
`filament_settings_id`. Only the `setting_id` half is enforced, and nothing violates it; the other two
|
|
||||||
are unchecked and plenty of bases still carry them. Do not copy that from a neighbouring file.
|
|
||||||
- Every `@System` must be `"instantiation": "true"`. DREMC ships `@System` presets set to `"false"`,
|
|
||||||
which therefore ship but can never be selected; no check catches it.
|
|
||||||
- A duplicated brand `@base` across bundles is normal and intentional (`Fiberon PA6-CF @base` exists in
|
|
||||||
both the library and BBL with the same id, differing only in MVS) — bases never enter the preset
|
|
||||||
collection, so there is no duplicate-name error.
|
|
||||||
- You may inherit from an instantiated preset as well as from a base; it is common.
|
|
||||||
|
|
||||||
## The two most common contributions
|
|
||||||
|
|
||||||
**A printer vendor tuning a generic.** Keep the `Generic X` base name so the alias shadows the library
|
|
||||||
preset on your printers, inherit `Generic X @System`, declare **no** `filament_id` (inheriting the
|
|
||||||
library's is correct — the product really is the library's generic), and give it a non-empty
|
|
||||||
`compatible_printers` in its own body:
|
|
||||||
|
|
||||||
```jsonc
|
|
||||||
// <Vendor>/filament/Generic PETG @Acme One 0.4 nozzle.json
|
|
||||||
{ "type": "filament", "name": "Generic PETG @Acme One 0.4 nozzle", "from": "system",
|
|
||||||
"instantiation": "true", "inherits": "Generic PETG @System",
|
|
||||||
"filament_flow_ratio": ["0.95"], "filament_max_volumetric_speed": ["10"],
|
|
||||||
"compatible_printers": ["Acme One 0.4 nozzle"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
**A printer vendor's own branded product.** Give it a `@base` root so `generate-id` can mint the id (see
|
|
||||||
[ids.md](ids.md) — inheriting `Generic X @System` directly makes the id unfixable by the tool), then one
|
|
||||||
instantiated leaf per printer in the same bundle. No `@System` shim: that is only for a product entering
|
|
||||||
OrcaFilamentLibrary.
|
|
||||||
|
|
||||||
```jsonc
|
|
||||||
// <Vendor>/filament/Acme Aura PETG @base.json — instantiation false, no setting_id
|
|
||||||
{ "type": "filament", "name": "Acme Aura PETG @base", "from": "system",
|
|
||||||
"instantiation": "false", "inherits": "fdm_filament_pet",
|
|
||||||
"filament_vendor": ["Acme"], "filament_type": ["PETG"] } // filament_id minted here
|
|
||||||
|
|
||||||
// <Vendor>/filament/Acme Aura PETG @Acme One 0.4 nozzle.json
|
|
||||||
{ "type": "filament", "name": "Acme Aura PETG @Acme One 0.4 nozzle", "from": "system",
|
|
||||||
"instantiation": "true", "inherits": "Acme Aura PETG @base",
|
|
||||||
"filament_max_volumetric_speed": ["11"],
|
|
||||||
"compatible_printers": ["Acme One 0.4 nozzle"] }
|
|
||||||
```
|
|
||||||
|
|
||||||
Omit `filament_settings_id` from new presets — it is runtime bookkeeping the app rewrites to the preset
|
|
||||||
name.
|
|
||||||
|
|
||||||
## `compatible_printers`
|
|
||||||
|
|
||||||
- **Library fallbacks:** empty `[]` or absent, so they are offered on all printers except where
|
|
||||||
[alias shadowing](#alias-shadowing) supplies a printer-specific tune.
|
|
||||||
- **Library printer-specific tunes:** non-empty, listing exact printer **variant** names. These can
|
|
||||||
supersede a same-alias fallback just like a tune in a printer vendor's bundle.
|
|
||||||
- **Instantiated filaments in every other vendor:** non-empty, listing exact printer **variant** names.
|
|
||||||
Enforced twice but not identically: the C++ `has_errors` reads the *flattened* config, so an inherited list satisfies it,
|
|
||||||
while the Python check reads the file's **own** key. Write the list in the file itself. This is the
|
|
||||||
most common filament CI failure.
|
|
||||||
- Emptying it to "make it apply everywhere" fails that check *and* creates a duplicate-`filament_id`
|
|
||||||
collision against the library generic on every printer.
|
|
||||||
- Copying a base's full printer list onto a nozzle-specific variant produces duplicate combobox entries —
|
|
||||||
a real shipped bug twice over.
|
|
||||||
|
|
||||||
## Alias shadowing
|
|
||||||
|
|
||||||
A printer-specific filament in either the library or a vendor bundle supersedes the library fallback
|
|
||||||
on the printers it lists. The matching key is the **alias**: the preset name up to the **first** `@`,
|
|
||||||
right-trimmed (no `@` → the whole name). So
|
|
||||||
`QIDI ABS-GF@Q2-Series` aliases to `QIDI ABS-GF`.
|
|
||||||
|
|
||||||
A library preset with an empty `compatible_printers` collects, into `m_excluded_from`, every printer named
|
|
||||||
by any same-alias preset that *has* a non-empty list, and is then hidden on those printers.
|
|
||||||
|
|
||||||
Two consequences:
|
|
||||||
|
|
||||||
- **Only an unrestricted library fallback can be shadowed.** Two printer-specific presets sharing
|
|
||||||
an alias do not exclude each other — overlapping lists for the same product trip the
|
|
||||||
duplicate-`filament_id` check instead.
|
|
||||||
- This is why adding `Generic PLA @<printer>` to a vendor silently removes the library `Generic PLA`
|
|
||||||
from that printer. Intended — and the reason a vendor tuning a generic must **keep the `Generic X`
|
|
||||||
base name**.
|
|
||||||
|
|
||||||
The literal spelling `Generic <mat> @System` is load-bearing beyond shadowing: `find_preset2` rewrites an
|
|
||||||
unresolved name containing "Generic" into that form and retries against the library, which is how 3MF
|
|
||||||
and project recovery works.
|
|
||||||
|
|
||||||
## `filament_id`, `filament_vendor`, `filament_type`
|
|
||||||
|
|
||||||
`filament_id` is minted from the triple `(filament_vendor, filament_type, name-before-first-@)`.
|
|
||||||
`filament_vendor` and `filament_type` are therefore **identity, not decoration** — editing either
|
|
||||||
re-mints the id. Read `docs/HLSD/filament_id.md` before changing any of them, and see
|
|
||||||
[ids.md](ids.md) for the tooling.
|
|
||||||
|
|
||||||
A filament with no resolvable `filament_id` anywhere in its `inherits` chain is a **hard load error** that
|
|
||||||
discards the vendor bundle. The id inherits across bundles, so a vendor's `Generic ABS @X` inheriting
|
|
||||||
`Generic ABS @System` gets the library's id for free; a vendor's own product must resolve its own.
|
|
||||||
|
|
||||||
- `filament_type` **must be a JSON array** — the one vector key the Python check enforces. A scalar
|
|
||||||
`"PP"` once hung the filament/printer selection UI.
|
|
||||||
- It is an **open** enum: an unlisted value is accepted silently and falls back to 190–300 °C defaults
|
|
||||||
and adhesion 1.0. Off-list values do ship. Prefer a value from `MaterialType::all()` in
|
|
||||||
`src/libslic3r/MaterialType.cpp`, or add a row there.
|
|
||||||
- Generics use `filament_vendor: ["Generic"]`, which `fdm_filament_common` already defaults to.
|
|
||||||
|
|
||||||
## `"nil"`
|
|
||||||
|
|
||||||
Legal in any key whose `ConfigOptionDef` is `nullable`. In a filament preset that is most of the
|
|
||||||
`filament_*` family, plus `long_retractions_when_ec` and `retraction_distances_when_ec`. About half are
|
|
||||||
the extruder overrides (`filament_retraction_length`, `filament_z_hop`, `filament_wipe`,
|
|
||||||
`filament_retract_*`, `filament_retraction_speed`, `filament_deretraction_speed`,
|
|
||||||
`filament_retraction_minimum_travel`, `filament_wipe_distance`, `filament_long_retractions_when_cut`,
|
|
||||||
`filament_retraction_distances_when_cut`, …), where `nil` means *keep the printer/extruder's own value*.
|
|
||||||
The rest are ordinary nullable options (`filament_flow_ratio`, `filament_flush_temp`,
|
|
||||||
`filament_adaptive_volumetric_speed`, …) where it means *unset*.
|
|
||||||
|
|
||||||
Anywhere else it throws `Deserializing nil into a non-nullable object`. To not set a non-nullable key,
|
|
||||||
omit it — do not write `nil`.
|
|
||||||
|
|
||||||
## What to review per nozzle
|
|
||||||
|
|
||||||
Across `@X` / `@X 0.N nozzle` sibling pairs the keys that differ, most often first, are
|
|
||||||
`filament_max_volumetric_speed`, `filament_retraction_length`, `slow_down_min_speed`,
|
|
||||||
`filament_flow_ratio`, `slow_down_layer_time`, `nozzle_temperature` and `pressure_advance`.
|
|
||||||
`filament_cost`, `filament_density`, `filament_type` and `filament_vendor` belong on the `@base` and
|
|
||||||
should not appear in a printer tune.
|
|
||||||
|
|
||||||
Use measured values for the material, hotend, extruder and nozzle combination. Neither maximum
|
|
||||||
volumetric speed nor pressure advance has a universal nozzle-only lookup table. When cloning a
|
|
||||||
0.4 preset for a 0.2 nozzle, explicitly revisit flow limits; do not infer a pressure-advance value
|
|
||||||
or a required direction of change from diameter alone.
|
|
||||||
|
|
||||||
## Style
|
|
||||||
|
|
||||||
Overrides, not full copies: a typical instantiated filament preset carries around a dozen non-meta keys,
|
|
||||||
and a library leaf two or three. Presets that restate fifty-plus keys from their parent do still ship —
|
|
||||||
Phrozen's single filament preset is that style — but they are the pattern to move away from, not to
|
|
||||||
copy. Commit `6943b6ddc3` is the stated model (flip true bases to `instantiation: "false"`, strip
|
|
||||||
`compatible_printers`/`setting_id`/`filament_settings_id`, add `renamed_from` on the survivor).
|
|
||||||
|
|
||||||
Prefer the library's `fdm_filament_*` bases over a vendor-local copy. Phrozen's local
|
|
||||||
`fdm_filament_common` has drifted from the library's.
|
|
||||||
|
|
||||||
Canonical key order, written by `orca_profile_tool.py normalize` when it rewrites a file: `type`, `name`,
|
|
||||||
`renamed_from`, `inherits`, `from`, `setting_id`, `filament_id`, `instantiation`, then everything else in
|
|
||||||
the order you wrote it. Not enforced — a file that leads with `compatible_printers` passes `check`.
|
|
||||||
|
|
||||||
**Every vector-typed (`co*s`) key must be a JSON array.** Only `filament_type` is an outright error, but
|
|
||||||
`normalize` silently arrayifies five more (`filament_cost`, `filament_density`,
|
|
||||||
`temperature_vitrification`, `filament_max_volumetric_speed`, `filament_vendor`) and `check` fails when
|
|
||||||
it would. Every other vector key is on you — including `filament_start_gcode`, `filament_end_gcode`,
|
|
||||||
`filament_extruder_variant`, `compatible_printers` and the plate temperatures.
|
|
||||||
|
|
||||||
## Bed temperature is twelve keys, not one
|
|
||||||
|
|
||||||
There is no single "bed temperature". Which plate key applies depends on `curr_bed_type`, whose six
|
|
||||||
selectable values (`btPC`, `btEP`, `btPEI`, `btPTE`, `btPCT`, `btSuperTack`; `btDefault` maps to no key)
|
|
||||||
`get_bed_temp_key()` turns into `cool_plate_temp`, `eng_plate_temp`, `hot_plate_temp`,
|
|
||||||
`textured_plate_temp`, `textured_cool_plate_temp` and `supertack_plate_temp` — each with an
|
|
||||||
`*_initial_layer` twin.
|
|
||||||
|
|
||||||
`textured_cool_plate_temp` is the one most often forgotten. A printer with `support_multi_bed_types` off
|
|
||||||
hides the selector, and the printer preset's
|
|
||||||
`default_bed_type` decides which plate is selected for it, but `curr_bed_type` can still hold a stale
|
|
||||||
value carried over from another printer — so set every plate the printer plausibly has, as the sibling
|
|
||||||
presets in the bundle do.
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
# `setting_id` and `filament_id`
|
|
||||||
|
|
||||||
Orca-generated ids are deterministic hashes of identity. **Never invent an id or copy a sibling's
|
|
||||||
`setting_id`.** Use `scripts/orca_profile_tool.py`; the two special cases are
|
|
||||||
[a wrongly inherited filament id](#what-generate-id-does-and-does-not-fix) and
|
|
||||||
[BBL's authoritative setting ids](#bbls-exception-precisely).
|
|
||||||
|
|
||||||
`docs/HLSD/filament_id.md` is the authoritative design document for `filament_id` — the id landscape, the
|
|
||||||
checks CI runs, and the Bambu catalog map. This page is the tooling half.
|
|
||||||
|
|
||||||
| | `setting_id` | `filament_id` |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Identifies | one selectable preset | one filament **product** |
|
|
||||||
| Key hashed | `<vendor folder>/<type>/<name>` | `filament_product/<filament_vendor>/<filament_type>/<name-before-@>` |
|
|
||||||
| Shape | 16 base62 chars | `OF` + 6 base62 chars |
|
|
||||||
| Required on | every `instantiation: "true"` preset | every **instantiated** filament, own or inherited |
|
|
||||||
| Forbidden on | bases (`instantiation != "true"`) | — (a base is exactly where it belongs) |
|
|
||||||
| Scope | globally unique across the tree | shared by every variant of the product, in every bundle |
|
|
||||||
|
|
||||||
`<type>` is `machine` / `process` / `filament` — the vendor is the **folder** name (`BBL`), not the
|
|
||||||
display name (`Bambulab`). Renaming a preset changes its `setting_id`; renaming a filament, or editing
|
|
||||||
its `filament_vendor` or `filament_type`, also changes its `filament_id`.
|
|
||||||
|
|
||||||
## The tool
|
|
||||||
|
|
||||||
Use `scripts/orca_profile_tool.py` with a subcommand:
|
|
||||||
|
|
||||||
| Command | Does |
|
|
||||||
| --- | --- |
|
|
||||||
| `check` | everything CI's `profile_tool` step runs — see [validation.md](validation.md) |
|
|
||||||
| `generate-id` | writes `setting_id` and `filament_id` |
|
|
||||||
| `normalize` | rewrites profile files into their canonical shape |
|
|
||||||
| `trim` | deletes profile files no `<vendor>.json` list references |
|
|
||||||
| `update-index` | rebuilds the `*_list` sections from the files on disk |
|
|
||||||
|
|
||||||
The order after adding, renaming or deleting files — each step feeds the next, so it is not
|
|
||||||
interchangeable — is `normalize` → `update-index` → `generate-id` → `check`.
|
|
||||||
The [authoring workflow](../SKILL.md#creating-or-modifying-a-profile) has the commands.
|
|
||||||
|
|
||||||
> **`trim` deletes.** It removes every profile file the index does not list — including the one you just
|
|
||||||
> added and have not registered yet. Register first, or skip `trim` entirely; it is a cleanup sweep, not
|
|
||||||
> part of landing a profile. Preview with `--dry-run`.
|
|
||||||
|
|
||||||
**Register, then mint.** The `filament_id` pass reads `<Vendor>.json`'s `filament_list`, not the
|
|
||||||
filesystem (the `setting_id` pass walks the filesystem, so a bundle whose index has not landed yet is
|
|
||||||
still assignable). A new filament file is therefore invisible to `generate-id`'s filament_id pass until
|
|
||||||
it is registered — its `setting_id` is written regardless.
|
|
||||||
|
|
||||||
- `--dry-run` works on every writing command (`generate-id`, `normalize`, `trim`, `update-index`)
|
|
||||||
and writes nothing.
|
|
||||||
- `--filament-id` / `--setting-id` narrow `generate-id`; they exclude each other, and passing neither
|
|
||||||
writes both.
|
|
||||||
- `--vendor` is repeatable and narrows **only what is written** — the id is a function of the triple
|
|
||||||
alone, so a narrowed run writes exactly what a full run would. An unknown vendor exits 1 before any
|
|
||||||
write. `--vendor` on `check` narrows the per-vendor checks only; the `setting_id` and `filament_id`
|
|
||||||
passes stay tree-wide.
|
|
||||||
- `--profiles DIR` points any command at another tree — see
|
|
||||||
[Checking a copy of the tree](validation.md#checking-a-copy-of-the-tree).
|
|
||||||
- `--profile-type` narrows `normalize`, `trim` and `update-index` to `machine_model`, `process`,
|
|
||||||
`filament` or `machine`.
|
|
||||||
- Exit codes: 0 clean, 1 errors found (`generate-id` still writes what it could), 2 argparse misuse.
|
|
||||||
- Output is ANSI-coloured; searching for the literal `[ERROR]` still works.
|
|
||||||
|
|
||||||
`generate-id` is **idempotent and byte-preserving** — BOM and CRLF kept, one key line touched per pass.
|
|
||||||
A legitimate `generate-id` diff is one or two changed lines per file: a new instantiated filament gets
|
|
||||||
both a `filament_id` and a `setting_id`, and a BBL file with a misspelled `settings_id` has that line
|
|
||||||
dropped and its value restored under the right key. `normalize` is the opposite by design — it rewrites
|
|
||||||
whole files into canonical shape — which is why `check` demands it already be a no-op. Some bundles have
|
|
||||||
CRLF committed (OrcaFilamentLibrary, Anycubic and RH3D among them), so a `normalize` pass there rewrites
|
|
||||||
every line — read the diff before committing it.
|
|
||||||
|
|
||||||
On a clean tree `check` and `generate-id --dry-run` both exit 0 with zero findings. That is the
|
|
||||||
baseline to restore before opening a PR.
|
|
||||||
|
|
||||||
## What `generate-id` does and does not fix
|
|
||||||
|
|
||||||
Writes:
|
|
||||||
|
|
||||||
- a `setting_id` into any instantiated preset that lacks one, or whose value does not match the formula;
|
|
||||||
- strips a `setting_id` from a base;
|
|
||||||
- deletes the misspelled `settings_id` key;
|
|
||||||
- a `filament_id` into the id-less **root(s)** of an instantiated filament that resolves none;
|
|
||||||
- rewrites a **declared** `filament_id` that is not the mint of its own triple.
|
|
||||||
|
|
||||||
Refuses to write (reports only): a base62 collision between two products, an empty `filament_vendor` or
|
|
||||||
`filament_type`, a broken `inherits` chain, roots of one filament resolving divergent `(vendor, type)`
|
|
||||||
pairs.
|
|
||||||
|
|
||||||
**Does not fix: a preset that *inherits* a wrong `filament_id`.** This is check 2b, and it is the trap
|
|
||||||
most likely to bite. It happens when a branded filament inherits a generic for its settings:
|
|
||||||
|
|
||||||
```jsonc
|
|
||||||
{ "name": "Phrozen Aura PETG @Phrozen Arco 0.4 nozzle",
|
|
||||||
"inherits": "Generic PETG @System" } // resolves the OFL generic's id — wrong product
|
|
||||||
```
|
|
||||||
|
|
||||||
The preset resolves *an* id, so `generate-id` neither inserts nor rewrites, and `check` fails with
|
|
||||||
`inherits filament_id "X" but its own triple "V/T/N" mints "Y"`.
|
|
||||||
|
|
||||||
Two fixes, in order of preference:
|
|
||||||
|
|
||||||
1. **Give the product a `@base` root** inheriting a material base (`fdm_filament_pet`,
|
|
||||||
`fdm_filament_pla`, …). No `fdm_filament_*` base carries a `filament_id`, so the filament now resolves
|
|
||||||
none and `generate-id` mints it for you. This is also the shape the rest of the tree uses.
|
|
||||||
2. **Declare the tool-computed key on the preset itself.** Use the expected value reported by `check`
|
|
||||||
or compute it with the function below; this is not a manually chosen id. Make sure the preset
|
|
||||||
resolves the right `filament_vendor` and `filament_type` first — with
|
|
||||||
neither set, the triple resolves through the generic parent and the branded product is minted
|
|
||||||
under vendor `Generic`. If you need the id before the file exists:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 -c "import sys; sys.path.insert(0,'scripts'); from orca_profile_tool import generate_filament_id as g; print(g('Polymaker','PLA','PolyLite PLA'))"
|
|
||||||
# -> OF5CgdDq
|
|
||||||
```
|
|
||||||
|
|
||||||
The quoting works unchanged in cmd and PowerShell; only swap `python3` for `py -3`.
|
|
||||||
|
|
||||||
The `setting_id` equivalent is `generate_preset_setting_id('<vendor folder>', '<type>', '<name>')`.
|
|
||||||
|
|
||||||
## BBL's exception, precisely
|
|
||||||
|
|
||||||
`RESERVED_VENDORS = {"BBL"}` covers **`setting_id` assignment only**, keyed on the *folder* name:
|
|
||||||
|
|
||||||
- The tool never mints or replaces a BBL `setting_id`. A new instantiated BBL preset with no
|
|
||||||
`setting_id` therefore **cannot be fixed by the tool**, yet the presence rule still applies to it —
|
|
||||||
carry over Bambu's authoritative id by hand.
|
|
||||||
- BBL is not exempt from anything else: bases still get their `setting_id` stripped, ids must still be
|
|
||||||
globally unique, and BBL `filament_id`s are minted like everyone else's — every one of them is an
|
|
||||||
`OF*`.
|
|
||||||
|
|
||||||
## Ids other systems compose
|
|
||||||
|
|
||||||
No id from another system is the mint of a triple, so `check` rejects it like any other bad id — same
|
|
||||||
error, same remedy, whoever wrote it. Three such spaces exist near the tree; recognise them so you do
|
|
||||||
not copy one into a profile:
|
|
||||||
|
|
||||||
- **Bambu's `GF*` catalog** — external and opaque, correlated to Orca's ids by the generated
|
|
||||||
`resources/printers/bambu_filament_ids.json`. `GF` is a *prefix*, not a spelling the tree avoids: most
|
|
||||||
BBL `setting_id`s start with `G`, and `blacklist.json` and
|
|
||||||
`BBL/filament/filaments_color_codes.json` both reference Bambu catalog ids by design. The rule is
|
|
||||||
about `filament_id` and nothing else.
|
|
||||||
- **Qidi's `QD_*`** — composed at runtime by the box (`QD_<series>_<vendor>_<typeidx>`), not a preset id.
|
|
||||||
- **`P` + 7 hex, and `"null"`** — what `CreatePresetsDialog.cpp` gives a *user*-created filament.
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
`python3 -m unittest discover -s scripts/tests -t scripts` (`py -3 -m …` on Windows). Note the
|
|
||||||
`-t scripts` argument; without it the imports fail. CI runs them as the first, non-`continue-on-error`
|
|
||||||
step of the profile job — see [validation.md](validation.md#ci).
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
# Printer models and variants
|
|
||||||
|
|
||||||
Both live in `resources/profiles/<Vendor>/machine/*.json`; models go in `machine_model_list`, variants
|
|
||||||
and shared bases in `machine_list`. Every one of them is registered. Some vendors (Elegoo, Eryone,
|
|
||||||
InfiMech, FlyingBear) nest a further subfolder under `machine/`, so recurse rather than globbing
|
|
||||||
`machine/*.json`.
|
|
||||||
|
|
||||||
## A `machine_model` is not a config preset
|
|
||||||
|
|
||||||
It is parsed by a hand-written key switch, and only these keys are stored (`version` and `url` are
|
|
||||||
matched and discarded):
|
|
||||||
|
|
||||||
`name`, `model_id`, `nozzle_diameter`, `machine_tech`, `family`, `bed_model`, `bed_texture`,
|
|
||||||
`hotend_model`, `default_materials`, `not_support_bed_type`, `image_bed_type`,
|
|
||||||
`bottom_texture_end_name`, `bottom_texture_rect`, `bottom_texture_rect_longer`, `middle_texture_rect`,
|
|
||||||
`use_double_extruder_default_texture`.
|
|
||||||
|
|
||||||
**Everything else is silently dropped.** Only `name` and `nozzle_diameter` are required. Dead keys ship
|
|
||||||
on real models today — `url`, `default_bed_type`, even a `desciption` typo — so a neighbour carrying a
|
|
||||||
key is no evidence it does anything. Printer config options belong on the `machine` preset, never here.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "machine_model",
|
|
||||||
"name": "Phrozen Arco",
|
|
||||||
"machine_tech": "FFF",
|
|
||||||
"family": "Phrozen",
|
|
||||||
"model_id": "Phrozen Arco",
|
|
||||||
"nozzle_diameter": "0.4",
|
|
||||||
"bed_model": "Phrozen Arco_buildplate_model.stl",
|
|
||||||
"bed_texture": "Phrozen Arco_buildplate_texture.svg",
|
|
||||||
"hotend_model": "",
|
|
||||||
"default_materials": "Generic PLA @Phrozen Arco 0.4 nozzle"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| Field | Notes |
|
|
||||||
| --- | --- |
|
|
||||||
| identity | **the `name` of the `machine_model_list` entry**, which is what a variant's `printer_model` must equal. `check_name_consistency` forces it to equal the file's `name`, so they coincide. |
|
|
||||||
| `model_id` | a *separate* cloud/device printer type. Optional, and not required to be unique. Not the model's identity. Changing it changes device matching. |
|
|
||||||
| `machine_tech` | only `starts_with("SL")` means SLA; everything else is FFF. Write `FFF`; a few models write `FGF`, which is a label with no effect. |
|
|
||||||
| `nozzle_diameter` | `;`-separated string, one token per available size. Order is free (Qidi writes `0.4;0.2;0.6;0.8` to put the default first). This list is the authoritative set of legal `printer_variant` values. |
|
|
||||||
| `default_materials` | `;`-separated filament **preset names**. Used to preselect in the wizard *and* by `PresetBundle::load_installed_filaments` to auto-install a printer's filaments on first run, so a dangling entry costs a real user a filament. Not `,`; case-sensitive (`@System`). `check` fails on a dangling name here or in `default_filament_profile`. |
|
|
||||||
| `family` | a wizard grouping label only; give every model one. |
|
|
||||||
|
|
||||||
### Assets
|
|
||||||
|
|
||||||
`bed_model`, `bed_texture` and `hotend_model` are paths relative to the **vendor folder** (by id).
|
|
||||||
Majority convention: `<Model>_buildplate_model.stl` and `<Model>_buildplate_texture.svg`. An empty string
|
|
||||||
is the legal "none", and is the norm for `hotend_model`.
|
|
||||||
|
|
||||||
**Nothing checks that the file exists.** A missing `hotend_model` falls back to
|
|
||||||
`resources/profiles/hotend.stl`; a missing `bed_model`/`bed_texture` just renders nothing. Broken
|
|
||||||
references already ship. Verify by hand.
|
|
||||||
|
|
||||||
Every model also has a `<Model>_cover.png` in the vendor folder — treat it as required, not optional.
|
|
||||||
240×240 is the cap `scripts/optimize_cover_images.py` enforces and the size most covers already use.
|
|
||||||
A missing cover degrades to a placeholder in both the wizard and the sidebar.
|
|
||||||
|
|
||||||
## The `machine` variant
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"type": "machine",
|
|
||||||
"name": "Phrozen Arco 0.4 nozzle",
|
|
||||||
"inherits": "fdm_machine_common",
|
|
||||||
"from": "system",
|
|
||||||
"setting_id": "lvaYKTUZr5C9jSwk",
|
|
||||||
"instantiation": "true",
|
|
||||||
"printer_model": "Phrozen Arco",
|
|
||||||
"printer_variant": "0.4",
|
|
||||||
"nozzle_diameter": ["0.4"],
|
|
||||||
"default_print_profile": "0.20mm Standard @Phrozen Arco 0.4 nozzle",
|
|
||||||
"default_filament_profile": ["Generic PLA @Phrozen Arco 0.4 nozzle"],
|
|
||||||
"printable_area": ["0x0", "300x0", "300x300", "0x300"],
|
|
||||||
"printable_height": "300"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Minimum viable key set: `type`, `name`, `from`, `instantiation`, `setting_id`, `inherits`,
|
|
||||||
`printer_model`, `printer_variant`, `nozzle_diameter`, `printable_area`, `printable_height`,
|
|
||||||
`default_print_profile`. The four keys without which the preset will not load at all are `name`,
|
|
||||||
`instantiation`, `printer_model` and `printer_variant`; `default_filament_profile` is an array
|
|
||||||
(`["Generic PLA @System"]`) and the model's `default_materials` a `;`-separated string. Unlike a
|
|
||||||
`machine_model`, a `machine` **is** config-loaded, so a key belonging to another preset type is a
|
|
||||||
reported error (a misspelled key is still silent).
|
|
||||||
|
|
||||||
### `printer_variant` — three hard rules
|
|
||||||
|
|
||||||
1. Non-empty, and an exact member of the model's `;`-separated `nozzle_diameter` list.
|
|
||||||
2. `printer_model` non-empty and naming a model of this vendor.
|
|
||||||
3. In validation mode, for instantiated presets only: split `printer_variant` on `+`, each token must
|
|
||||||
start with a number (a trailing non-numeric suffix such as `HF` is ignored), and the resulting **set**
|
|
||||||
must equal `set(nozzle_diameter)`.
|
|
||||||
|
|
||||||
Rules 1 and 2 are loader-enforced — failing either drops the preset *and* the whole bundle. Rule 3 only
|
|
||||||
raises a validation error: the preset still loads, but the validator exits non-zero.
|
|
||||||
|
|
||||||
`nozzle_diameter` lists one entry **per physical nozzle**; `printer_variant` lists the **distinct**
|
|
||||||
diameters joined with `+`. Snapmaker U1 is the worked case: `["0.4","0.4","0.6","0.6"]` against
|
|
||||||
`"0.4+0.6"` — it passes because the comparison is on sets.
|
|
||||||
|
|
||||||
The conventional values are `0.2`, `0.25`, `0.4`, `0.5`, `0.6`, `0.8` and `1.0`. Suffixed forms
|
|
||||||
(`0.4HF`, `0.6HF`, `0.8HF`, `0.4HS`) are Flashforge-only and the `+` form is rare. A variant is **not**
|
|
||||||
required to be unique within a model — Volumic ships `EXO42 IDRE`, `… COPY MODE` and `… MIRROR MODE` all
|
|
||||||
at `0.4` under the one model `EXO42 IDRE`.
|
|
||||||
|
|
||||||
The converse is **unchecked**: a nozzle size in the model's list with no matching variant is offered in
|
|
||||||
the wizard and resolves to nothing. `Wanhao France`'s `D12 500 PRO M2 DIRECT` ships that bug today.
|
|
||||||
|
|
||||||
### Other fields worth knowing
|
|
||||||
|
|
||||||
- `default_print_profile` is a **scalar**, matched by exact preset name. Not a `;` list. The named
|
|
||||||
process must be compatible with this printer through its resolved list or condition.
|
|
||||||
`validate_slice` attempts to select it and rejects generic Default fallbacks, but compatibility
|
|
||||||
updates can choose another compatible preset. Check the exact default reference yourself.
|
|
||||||
- `default_filament_profile` is an **array**, one name per element.
|
|
||||||
- `printable_area` is an array of `"XxY"` strings — four points for a rectangle, one per segment for a
|
|
||||||
delta or circular bed.
|
|
||||||
- `gcode_flavor` is usually set once in the base; `klipper`, `marlin`, `marlin2` and `reprapfirmware`
|
|
||||||
cover nearly every shipped printer.
|
|
||||||
- `printer_settings_id` is junk — most files carrying it disagree with their own name. Do not copy it
|
|
||||||
when cloning a bundle.
|
|
||||||
- `min_layer_height` / `max_layer_height` are **machine** keys (per extruder), never process keys.
|
|
||||||
|
|
||||||
## Bases
|
|
||||||
|
|
||||||
Nearly every machine-bearing vendor registers a base literally named `fdm_machine_common`, and Klipper
|
|
||||||
vendors add `fdm_klipper_common` on top of it. Two levels is the usual depth.
|
|
||||||
|
|
||||||
**There is no leading-underscore convention for bases.**
|
|
||||||
|
|
||||||
## Adding a printer to an existing bundle
|
|
||||||
|
|
||||||
1. Choose the names first — model, variant(s), process(es); everything else references them.
|
|
||||||
2. Add the model (`machine_model_list`) and one `machine` variant per nozzle; the minimum key sets are
|
|
||||||
above. Bed assets and `<Model>_cover.png` go directly in `<Vendor>/`.
|
|
||||||
3. Add at least one process per variant naming it in `compatible_printers`
|
|
||||||
([process-profiles.md](process-profiles.md#adding-a-quality-tier-or-a-nozzles-processes)).
|
|
||||||
4. Register everything (or run `update-index`), bump the version, run the id tool, validate.
|
|
||||||
|
|
||||||
## Adding a nozzle variant
|
|
||||||
|
|
||||||
1. Extend the model's `nozzle_diameter` (`"0.4"` → `"0.4;0.6"`).
|
|
||||||
2. Add the variant preset. Either inherit the shared base (the usual choice) or the 0.4 sibling (Elegoo,
|
|
||||||
BBL, Prusa and Qidi do this — smaller diff, but the sibling's edits now reach this file too).
|
|
||||||
3. Override what actually changes with nozzle: `nozzle_diameter`, `printer_variant`,
|
|
||||||
`default_print_profile`, `default_filament_profile`, `min_layer_height`/`max_layer_height`, and
|
|
||||||
retraction if the vendor tunes it.
|
|
||||||
4. Add at least one process for the new nozzle — see [process-profiles.md](process-profiles.md).
|
|
||||||
5. Register both, bump the version, run the id tool, validate.
|
|
||||||
|
|
||||||
## Multi-extruder, IDEX and tool-changers
|
|
||||||
|
|
||||||
Per-extruder vectors are **silently resized** to the nozzle count, with no error. Padding repeats the
|
|
||||||
**first** value, not the last — `["0.4","0.6"]` on a 4-nozzle machine becomes `0.4, 0.6, 0.4, 0.4`.
|
|
||||||
Longer vectors are truncated.
|
|
||||||
|
|
||||||
Note the two sizing families: the plain per-extruder keys (`extruder_offset`, `extruder_colour`,
|
|
||||||
`extruder_printable_height`, `min_layer_height`, `max_layer_height`, `nozzle_diameter`) are sized to the
|
|
||||||
extruder count, while `printer_options_with_variant_1` (`retraction_length`, `z_hop`, `wipe`,
|
|
||||||
`nozzle_type`, the rest of the retraction family) is sized to `printer_extruder_variant` instead.
|
|
||||||
|
|
||||||
- Give **one entry per extruder** for ordinary per-extruder vectors such as `extruder_offset`,
|
|
||||||
`extruder_colour`, `min_layer_height` and `max_layer_height`; size the variant-dependent family
|
|
||||||
to `printer_extruder_variant` instead.
|
|
||||||
A single `["0x0"]` `extruder_offset` on a dual or multi-tool machine — which already ships — pads every
|
|
||||||
toolhead to the same offset, so the offset never applies.
|
|
||||||
- Overriding `nozzle_diameter` to a different count without re-stating every per-extruder vector is the
|
|
||||||
other half of the trap — `Snapmaker U1 (0.4+0.6 nozzle)` inherits 5-entry vectors against 4 nozzles.
|
|
||||||
|
|
||||||
Copy targets: `Custom/machine/fdm_toolchanger_common.json` + `Custom/machine/MyToolChanger 0.4
|
|
||||||
nozzle.json` (a clean minimal variant on a base that gives every vector five entries), and
|
|
||||||
`Ratrig/machine/RatRig V-Core 4 IDEX 300 0.4 nozzle.json` for IDEX. The BBL extruder-variant machinery
|
|
||||||
(`extruder_variant_list`, `printer_extruder_id`, `default_nozzle_volume_type`) is used by a handful of
|
|
||||||
vendors — do not copy it into a new bundle (`nozzle_volume_type` itself is not a machine-preset key).
|
|
||||||
|
|
||||||
## Custom G-code
|
|
||||||
|
|
||||||
The keys are `machine_start_gcode`, `machine_end_gcode`, `change_filament_gcode`,
|
|
||||||
`machine_pause_gcode`, `before_layer_change_gcode` and `layer_change_gcode`. Both a single string with
|
|
||||||
embedded `\n` and a JSON array of lines are legal and both are in use — do not convert one into the
|
|
||||||
other. Conditionals are `{if …}` / `{elsif …}` / `{else}` / `{endif}`; `{elsif}` is rare but real (Qidi's
|
|
||||||
`layer_change_gcode` uses it).
|
|
||||||
|
|
||||||
Placeholder errors only surface when the config is actually expanded, which means `validate_slice`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./scripts/check_profile.sh --vendor "<Vendor>" validate_slice
|
|
||||||
# Windows: scripts\check_profile.bat -Vendor "<Vendor>" validate_slice
|
|
||||||
```
|
|
||||||
|
|
||||||
What the sweep covers is in [validation.md](validation.md#validate_slice); no `CP TOOLCHANGE START` in
|
|
||||||
the output means `change_filament_gcode` never expanded.
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
# Process profiles
|
|
||||||
|
|
||||||
Processes live in `resources/profiles/<Vendor>/process/` — selectable leaves and shared bases alike, and
|
|
||||||
every one of them is registered in `process_list`. There are no global processes shared across vendors.
|
|
||||||
|
|
||||||
## Naming
|
|
||||||
|
|
||||||
`"<layer height>mm <quality> @<target>"` — near-universal, so match it.
|
|
||||||
|
|
||||||
Follow the bundle's existing quality vocabulary. BBL's common ladder relates the quality word to
|
|
||||||
the layer-height / nozzle ratio; it is a naming convention, not a loader constraint:
|
|
||||||
|
|
||||||
| Quality | Ratio | 0.2 nozzle | 0.4 | 0.6 | 0.8 |
|
|
||||||
| --- | --- | --- | --- | --- | --- |
|
|
||||||
| Extra Fine | 0.2× | — | 0.08 | — | — |
|
|
||||||
| Fine | 0.3× | 0.06 | 0.12 | 0.18 | 0.24 |
|
|
||||||
| Optimal | 0.4× | 0.08 | 0.16 | 0.24 | 0.32 |
|
|
||||||
| Standard | 0.5× | 0.10 | 0.20 | 0.30 | 0.40 |
|
|
||||||
| Draft | 0.6× | 0.12 | 0.24 | 0.36 | 0.48 |
|
|
||||||
| Extra Draft | 0.7× | 0.14 | 0.28 | 0.42 | 0.56 |
|
|
||||||
|
|
||||||
This is the `fdm_process_single_<lh>_nozzle_<n>` ladder; 0.4 is commonly the unsuffixed nozzle default.
|
|
||||||
Match neighbouring names rather than renaming shipped tiers to fit the table.
|
|
||||||
|
|
||||||
The `@target` is a human label, not a reference: most do not equal any real printer variant name.
|
|
||||||
Compatibility comes from the resolved list or condition, not this label.
|
|
||||||
|
|
||||||
## Shape
|
|
||||||
|
|
||||||
A selectable leaf's only truly universal keys are `type`, `setting_id`, `name` and `instantiation`;
|
|
||||||
`inherits` and `from` are near-universal — plus compatibility. No slicing key is universal; even
|
|
||||||
`layer_height` is more often inherited than restated. A base has `type`, `name`, `instantiation`, almost
|
|
||||||
always `from`, and **no** `setting_id`.
|
|
||||||
|
|
||||||
**Target shape: a 7-key leaf.** `OrcaArena` is the cleanest model —
|
|
||||||
`fdm_process_common` → `fdm_process_arena_common` → `fdm_process_arena_<lh>_nozzle_<n>` → leaf, where the
|
|
||||||
leaf carries only `type`, `name`, `inherits`, `from`, `setting_id`, `instantiation`,
|
|
||||||
`compatible_printers`, and the per-nozzle base holds the layer height and all eight line widths.
|
|
||||||
|
|
||||||
BBL, WonderMaker and Z-Bolt are uniform in *layering* — every leaf inherits a base, names its printers
|
|
||||||
directly and holds no layer height of its own — but not in key count. Imitate BBL's layering, not its
|
|
||||||
content: its leaves carry doubled `print_extruder_variant` arrays that no single-variant vendor needs.
|
|
||||||
|
|
||||||
Nearly every vendor ships its own `fdm_process_common` as the inherits-less root. Those files are not
|
|
||||||
identical; copying another vendor's version into a new bundle is normal.
|
|
||||||
|
|
||||||
Beware leaf-inherits-leaf: Prusa chains several levels deep through sibling leaves, and Elegoo and
|
|
||||||
Flashforge do it too, so editing one selectable process silently changes others. Check a leaf's children
|
|
||||||
before editing it.
|
|
||||||
|
|
||||||
## Compatibility
|
|
||||||
|
|
||||||
Most leaves set `compatible_printers` directly; some inherit it from a base, and Prusa's fall through to
|
|
||||||
`compatible_printers_condition`. After resolving `inherits`, **every selectable process has one or the
|
|
||||||
other** — that is the invariant to review against. Unlike filaments, inheriting `compatible_printers` is
|
|
||||||
legitimate for a process, and no check enforces its presence.
|
|
||||||
|
|
||||||
- A non-empty `compatible_printers` makes `compatible_printers_condition` **dead code**. Use one or
|
|
||||||
the other.
|
|
||||||
- A condition that fails to parse means *compatible with everything* — a warning, not an error. A typo
|
|
||||||
widens compatibility instead of narrowing it.
|
|
||||||
- Matching is `boost::regex` **`regex_match`** — a full-string match, which is why every shipped
|
|
||||||
condition wraps its keyword in `.*`. Because it is boost rather than `std`, `.` also spans the newlines
|
|
||||||
inside `printer_notes`.
|
|
||||||
- A `printer_notes` keyword that prefixes another model's keyword matches both. Prusa guards it:
|
|
||||||
|
|
||||||
```
|
|
||||||
printer_notes=~/.*PRINTER_MODEL_COREONE[^_a-zA-Z0-9].*/ and nozzle_diameter[0]==0.4 and printer_notes=~/.*HF_NOZZLE.*/
|
|
||||||
```
|
|
||||||
|
|
||||||
The `[^_a-zA-Z0-9]` exists because `PRINTER_MODEL_COREONE_L` also contains `PRINTER_MODEL_COREONE`.
|
|
||||||
|
|
||||||
`compatible_printers` is almost always one element. A leaf listing a whole model family is where a newly
|
|
||||||
added printer is usually forgotten.
|
|
||||||
|
|
||||||
## What to review per nozzle
|
|
||||||
|
|
||||||
| Key group | Review |
|
|
||||||
| --- | --- |
|
|
||||||
| `line_width` and per-region widths | resolved widths suit the nozzle and layer height |
|
|
||||||
| `layer_height`, `initial_layer_print_height` | within the printer's limits |
|
|
||||||
| print speeds | consistent with flow limits and hardware tuning |
|
|
||||||
| shell layers, wall loops, accelerations, support Z distances | preserve the intended thickness, motion and support behavior |
|
|
||||||
|
|
||||||
**A common starting pattern is nozzle + 0.02 mm**: 0.22 / 0.42 / 0.62 / 0.82 / 1.02. In that pattern, at 0.4,
|
|
||||||
`inner_wall_line_width`, `sparse_infill_line_width`, `skin_infill_line_width` and
|
|
||||||
`skeleton_infill_line_width` widen to 0.45 and `initial_layer_line_width` to 0.5; at 0.2,
|
|
||||||
`initial_layer_line_width` widens to 0.25. Also derived, and easily missed:
|
|
||||||
`ironing_inset = line_width / 2` (0.11 / 0.21 / 0.31 / 0.41).
|
|
||||||
These are examples, not required values; preserve intentional vendor tuning and percentage/automatic
|
|
||||||
widths, and validate their resolved values.
|
|
||||||
|
|
||||||
`min_layer_height` and `max_layer_height` are machine keys — no process file sets them.
|
|
||||||
|
|
||||||
## Slice-time content checks
|
|
||||||
|
|
||||||
`Print::validate()` enforces four rules at slice time:
|
|
||||||
|
|
||||||
1. `initial_layer_print_height` ≤ min `nozzle_diameter`
|
|
||||||
2. `layer_height` ≤ min `nozzle_diameter` — *"Layer height cannot exceed nozzle diameter."*
|
|
||||||
3. `line_width` and the seven per-region widths (inner/outer wall, sparse infill, internal solid infill,
|
|
||||||
top surface, skin, skeleton) > `layer_height` — *"Line width too small"*. `support_line_width` only
|
|
||||||
when the object has support or a raft; `initial_layer_line_width` is never checked.
|
|
||||||
4. every width ≤ 5 × max `nozzle_diameter` — *"Line width too large"*
|
|
||||||
|
|
||||||
Two further rules cover `bridge_line_width` (≤ nozzle diameter; > `layer_height` unless `thick_bridges`
|
|
||||||
and `thick_internal_bridges` are both on). The sweep starts from printer defaults rather than
|
|
||||||
enumerating every process. **A new non-default process gets no dedicated slice coverage in CI.**
|
|
||||||
|
|
||||||
## What CI checks on a process
|
|
||||||
|
|
||||||
Structure, not content: `process_list` name consistency **and** index coverage the other way, two files
|
|
||||||
claiming one process name, the `extruder_clearance_radius` / `extruder_clearance_max_radius` conflict
|
|
||||||
pair, duplicate JSON keys, a file `normalize` would rewrite, and the five `setting_id` rules (the fifth
|
|
||||||
rejects the misspelled key `settings_id`). `compatible_printers` presence is checked for **filaments
|
|
||||||
only**.
|
|
||||||
|
|
||||||
Note the C++ loader derives a missing `setting_id` on the fly, so the validator will not fail a process
|
|
||||||
without one — only `orca_profile_tool.py check` catches it. Running the validator alone gives a false
|
|
||||||
all-clear.
|
|
||||||
|
|
||||||
## Silent failures specific to processes
|
|
||||||
|
|
||||||
- **Unknown or misspelled keys are discarded with no error and no warning.** They ship all over the
|
|
||||||
process tree, both plain typos (`inital_layer_height`, `tree_support_bramch_diameter_angle`,
|
|
||||||
`sparse_infill_patter`) and keys copied from other slicers that Orca never defined.
|
|
||||||
- Keys on the tool's `OBSOLETE_KEYS` list (`adaptive_layer_height`, `overhang_totally_speed`, …) are
|
|
||||||
rejected by `check`'s normalization pass across preset types; `normalize` removes them.
|
|
||||||
The additional per-key obsolete warnings read `filament/` only.
|
|
||||||
- A dangling `compatible_printers` inside an `instantiation: "false"` base is invisible to
|
|
||||||
`check_preset_references`: a base never becomes a `Preset` at all (its config goes into `config_maps`
|
|
||||||
and the loader returns early), so it is in no collection for the check to walk.
|
|
||||||
- Orphan bases that nothing inherits are scattered through the tree — usually the leftover of a
|
|
||||||
half-finished nozzle addition.
|
|
||||||
|
|
||||||
## Adding a quality tier or a nozzle's processes
|
|
||||||
|
|
||||||
1. Choose the layer height and quality label using the vendor's existing ladder.
|
|
||||||
2. If the vendor has per-nozzle bases, add one (`fdm_process_<vendor>_<lh>_nozzle_<n>`) with the layer
|
|
||||||
height, nozzle-appropriate line widths, `initial_layer_print_height` and `ironing_inset`.
|
|
||||||
3. Add the leaf: 7 keys, `compatible_printers` naming the exact printer variant(s).
|
|
||||||
4. Register both in `process_list`, parent first. Bump the version, run the id tool, validate.
|
|
||||||
5. Slice this process explicitly with its intended printer; the sweep gives non-default tiers no
|
|
||||||
dedicated coverage. If it is a printer's `default_print_profile`, verify the exact name and
|
|
||||||
resolved compatibility too — the sweep may fall back or select another compatible process.
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
# Reviewing a profile change
|
|
||||||
|
|
||||||
Start with delivery, identity and backward compatibility, then check the affected preset types.
|
|
||||||
The table highlights gaps that need human review. What CI *does* run:
|
|
||||||
[validation.md](validation.md).
|
|
||||||
|
|
||||||
| Not checked by CI | Consequence |
|
|
||||||
| --- | --- |
|
|
||||||
| The `version` bump | The change never reaches an upgrading user |
|
|
||||||
| A misspelled setting key | Setting silently has no effect |
|
|
||||||
| A filename Windows cannot check out, or one that differs from its `sub_path` only in case | Works on the author's machine, breaks the bundle on another platform |
|
|
||||||
| `bed_model` / `bed_texture` / `hotend_model` pointing at a missing asset | Bed renders as Custom, hotend falls back to the generic model |
|
|
||||||
| A nozzle size in a model's list with no matching variant | The size is offered and resolves to nothing |
|
|
||||||
| A non-default process | `validate_slice` gives non-default quality tiers no dedicated coverage |
|
|
||||||
| Whether the intended default survived compatibility selection | The sweep can select a different compatible preset |
|
|
||||||
| A dangling `compatible_printers` inside an `instantiation: "false"` base | A base never becomes a `Preset`, so the reference check never sees it (a bad `inherits` in a base *is* caught) |
|
|
||||||
| A `renamed_from` whose old name is still a live preset | The redirect is inert while a live preset carries that name |
|
|
||||||
| Per-extruder vector length on a multi-nozzle printer | Silently padded (with the **first** value) or truncated |
|
|
||||||
|
|
||||||
## 1. Was the vendor `version` bumped?
|
|
||||||
|
|
||||||
For **every** bundle whose folder the diff touches, `resources/profiles/<Vendor>.json` must have its
|
|
||||||
`version` incremented — last component, carrying `.99` into the third component. A library change
|
|
||||||
means bumping `OrcaFilamentLibrary.json`.
|
|
||||||
|
|
||||||
*Why:* nothing in CI checks it, and `PresetUpdater` reinstalls only when `vendor_ver < resource_ver` —
|
|
||||||
without a bump the change reaches neither an upgrading user nor the author's own running app.
|
|
||||||
|
|
||||||
## 2. Was the index rebuilt, and does the diff contain only this change?
|
|
||||||
|
|
||||||
`check` now fails on an unregistered file, on an index `update-index` would reorder, and on a file
|
|
||||||
`normalize` would rewrite — so a PR that skipped them arrives red, and you do not have to spot the
|
|
||||||
omission yourself. Three things are still yours:
|
|
||||||
|
|
||||||
- **The index diff belongs to this change.** `update-index` rewrites whole `*_list` sections. If the
|
|
||||||
bundle had drifted, the author's PR now carries someone else's reordering; ask for it in a separate
|
|
||||||
commit rather than reviewing it inline.
|
|
||||||
- **A deleted selectable preset needs a successor** as in item 4. `update-index` removes its
|
|
||||||
registration; `validate_custom` detects the break only for names covered by released fixtures.
|
|
||||||
- **`normalize` edits content, not just layout.** It drops `version` and `is_custom_defined` from preset
|
|
||||||
files, removes obsolete keys, deletes six print-speed keys from filament profiles, and resolves
|
|
||||||
`extruder_clearance_radius` against `extruder_clearance_max_radius` by keeping the larger.
|
|
||||||
Check that the keys it removed were meant to go.
|
|
||||||
|
|
||||||
Obsolete keys fail `check`'s normalization pass and should be removed with `normalize`.
|
|
||||||
`check` also reports per-key obsolete warnings for filament profiles in the selected vendors.
|
|
||||||
|
|
||||||
*Why:* the index is the loader's only entry point. Out-of-order entries fail with `can not find inherits`
|
|
||||||
and take the whole vendor bundle down; an unindexed file gets reviewed, merged and never loads.
|
|
||||||
|
|
||||||
## 3. Are ids generated, not written?
|
|
||||||
|
|
||||||
No hand-typed or copied `setting_id` / `filament_id`. Instantiated presets have a `setting_id`; bases do
|
|
||||||
not. `check` enforces all of that; what it cannot tell you is whether the identity *should* have moved.
|
|
||||||
|
|
||||||
A rewritten or removed `filament_id` means a product's identity moved — a rename, or an edited
|
|
||||||
`filament_vendor` / `filament_type` — and the old id is not forwarded anywhere. Confirm that was
|
|
||||||
intended, and that a new id is not a rename in disguise.
|
|
||||||
|
|
||||||
*Why:* a duplicate `filament_id` on one printer makes AMS spool matching a coin toss; a copied
|
|
||||||
`setting_id` breaks preset identity. See [ids.md](ids.md).
|
|
||||||
|
|
||||||
## 4. Does anything disappear for existing users?
|
|
||||||
|
|
||||||
A rename, a deletion, or a flip of `"instantiation": "true"` → `"false"` on a shipped preset removes the
|
|
||||||
name from the preset collection. It needs `renamed_from` on a successor — and only one preset may claim a
|
|
||||||
given old name. The claimed old name must **not** still be a live preset; the redirect is inert if it is.
|
|
||||||
|
|
||||||
*Why:* user presets inheriting it die with `can not find parent <name> for config <file>!`; 3MF-embedded
|
|
||||||
presets are dropped with no error at all. Commit `33923464ae` reverted exactly this for Cubicon;
|
|
||||||
`6943b6ddc3` redid it correctly. CI's `validate_custom` catches the shipped-name case — but not an inert
|
|
||||||
`renamed_from`.
|
|
||||||
|
|
||||||
## 5. Is `compatible_printers` right?
|
|
||||||
|
|
||||||
Exact printer **variant** names, non-empty on every instantiated filament outside OrcaFilamentLibrary
|
|
||||||
and written in the preset's own file — golden rule 6, with the flattened-vs-own-key trap in
|
|
||||||
[filament-profiles.md](filament-profiles.md#compatible_printers). Watch for a nozzle-specific variant that
|
|
||||||
inherited or copied the base's full printer list, and for two presets of one product with overlapping
|
|
||||||
lists — duplicate combobox entries and an ambiguous AMS match.
|
|
||||||
|
|
||||||
*Why:* real shipped bugs twice (`b7b3418baf` "showing up everywhere", `ff83aa41ef` duplicate Flashforge
|
|
||||||
entries).
|
|
||||||
|
|
||||||
## 6. Model ↔ variant ↔ process consistency
|
|
||||||
|
|
||||||
- New nozzle size → the model's `nozzle_diameter` list extended, a variant with a matching
|
|
||||||
`printer_variant`, and at least one process listing that variant.
|
|
||||||
- `default_print_profile` is one exact name (not a `;` list), and that process's resolved
|
|
||||||
compatibility list or condition includes this printer.
|
|
||||||
- `default_filament_profile` is an array of names that exist.
|
|
||||||
|
|
||||||
*Why:* an unlisted `printer_variant` is a hard bundle-load failure. Default process selection is
|
|
||||||
weaker: the sweep attempts the named default, then updates compatibility and rejects generic Default
|
|
||||||
fallbacks. Another compatible process can conceal a bad reference, so inspect it even after a pass.
|
|
||||||
|
|
||||||
## 7. Types and spellings
|
|
||||||
|
|
||||||
Every value a string or an array of strings; `filament_type` an array; `instantiation` the string
|
|
||||||
`"true"`/`"false"` — golden rule 7. Check index metadata and model `nozzle_diameter` especially;
|
|
||||||
wrong types there can abort loading for **every** vendor.
|
|
||||||
|
|
||||||
The part only a reviewer can do: check new setting keys against `src/libslic3r/PrintConfig.cpp`. A
|
|
||||||
misspelled key is silently discarded (rule 8), the single most common way a profile edit does nothing
|
|
||||||
while CI stays green.
|
|
||||||
|
|
||||||
## 8. Blast radius of a base edit
|
|
||||||
|
|
||||||
A change to `fdm_*_common.json` reaches every child at once. Ask which presets it touches — several
|
|
||||||
reverts in this repo are exactly this (`41d1b0d3c8`, `dc491166a8`). Also check whether the edited leaf has
|
|
||||||
children of its own: Prusa, Flashforge and Elegoo all chain leaf-inherits-leaf several levels deep.
|
|
||||||
|
|
||||||
## 9. Do the numbers make sense for the nozzle?
|
|
||||||
|
|
||||||
Check resolved widths and layer heights against the nozzle, and flow limits / pressure advance
|
|
||||||
against the actual hardware and material. The patterns in [process-profiles.md](process-profiles.md)
|
|
||||||
are examples, not mandatory values; [filament-profiles.md](filament-profiles.md) explains what to
|
|
||||||
revisit for a nozzle change. A cloned preset's unchanged MVS needs particular scrutiny.
|
|
||||||
|
|
||||||
Settings tuned for real hardware cannot be verified by reading the diff. Say so rather than approving
|
|
||||||
numbers nobody measured.
|
|
||||||
|
|
||||||
## 10. Asset references (not checked anywhere)
|
|
||||||
|
|
||||||
`bed_model`, `bed_texture`, `hotend_model` and `<Model>_cover.png` exist under
|
|
||||||
`resources/profiles/<vendor folder>/`. Broken references already ship; nothing checks them.
|
|
||||||
|
|
||||||
## 11. `default_materials` (checked by CI)
|
|
||||||
|
|
||||||
`check` fails on a `default_materials` / `default_filament_profile` name that resolves to no system
|
|
||||||
filament, so a dangling entry no longer reaches review. Scope the run while working on one vendor:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python3 scripts/orca_profile_tool.py check --vendor "<Vendor>" # py -3 on Windows
|
|
||||||
```
|
|
||||||
|
|
||||||
## 12. Per-extruder vector lengths (not checked)
|
|
||||||
|
|
||||||
One entry per extruder for the plain per-extruder vectors; the `printer_options_with_variant_1` keys are
|
|
||||||
sized to `printer_extruder_variant` instead. A wrong length is silently padded — repeating the **first**
|
|
||||||
value, not the last — or truncated. The two sizing families and the worked cases are in
|
|
||||||
[machine-profiles.md](machine-profiles.md#multi-extruder-idex-and-tool-changers).
|
|
||||||
|
|
||||||
## 13. Non-default processes get no slice coverage
|
|
||||||
|
|
||||||
`validate_slice` starts from printer defaults; it does not enumerate every process. Slice a new or
|
|
||||||
changed non-default tier explicitly with its intended printer.
|
|
||||||
|
|
||||||
## 14. Housekeeping worth a nit, not a block
|
|
||||||
|
|
||||||
`"from"` other than `"system"` (the preset-bundle loader ignores it, though the CLI's config-file loader
|
|
||||||
rejects anything but `system`/`user`/`User`), `printer_settings_id` copied from another
|
|
||||||
vendor, and a filename that disagrees with the preset's `name` (common; the loader keys off `name`).
|
|
||||||
|
|
||||||
## 15. Cross-platform filenames and paths (not checked)
|
|
||||||
|
|
||||||
Check for Windows-invalid characters, reserved device names, trailing path-component spaces/dots,
|
|
||||||
and case mismatches in `sub_path` or asset paths. See [cross-platform paths](validation.md#cross-platform-paths).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Reporting the review
|
|
||||||
|
|
||||||
A finding is: **one defect**, its file, what breaks at runtime or in CI, and the fix. Split independent
|
|
||||||
defects into separate findings even when they live in one file — five id problems in one bullet get one
|
|
||||||
fix and four survivors.
|
|
||||||
|
|
||||||
Severity discriminates only if it is earned:
|
|
||||||
|
|
||||||
| Severity | Means |
|
|
||||||
| --- | --- |
|
|
||||||
| blocker | the bundle fails to load, or a preset is unreachable at runtime |
|
|
||||||
| major | CI fails, or existing users lose a preset |
|
|
||||||
| minor | wrong-but-working: dead keys, `from`, naming, redundant overrides |
|
|
||||||
|
|
||||||
Compute every number and id (`orca_profile_tool.py`, a scripted count) or omit it — one invented count
|
|
||||||
makes a reader stop trusting the right ones. Report a command's result only if you ran it.
|
|
||||||
@@ -1,255 +0,0 @@
|
|||||||
# Validating profiles
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./scripts/check_profile.sh # everything CI runs
|
|
||||||
./scripts/check_profile.sh --vendor "<Vendor>" # fast loop
|
|
||||||
./scripts/check_profile.sh profile_tool validate_slice # named checks only
|
|
||||||
```
|
|
||||||
|
|
||||||
```bat
|
|
||||||
scripts\check_profile.bat :: the same three, on Windows
|
|
||||||
scripts\check_profile.bat -Vendor "<Vendor>"
|
|
||||||
scripts\check_profile.bat profile_tool validate_slice
|
|
||||||
```
|
|
||||||
|
|
||||||
`check_profile.bat` is a shim around `check_profile.ps1` — same checks, same order, same logs;
|
|
||||||
the flags take PowerShell spellings (`-Vendor`, `-ProfilesDir`, `-Validator`, `-Download`, `-Refresh`,
|
|
||||||
`-WorkDir`, `-LogLevel`) and positional check names are unchanged. `-p`, `-v` and `-l` are aliases, so
|
|
||||||
`-v Elegoo -l 2` reads the same on both platforms. It passes `-ExecutionPolicy Bypass` because a
|
|
||||||
default Windows client refuses to run a checked-out `.ps1` at all. The `.ps1` finds Python itself,
|
|
||||||
probing `py -3`, then `python`, then `python3`; run the tool by hand with `py -3` for the same reason.
|
|
||||||
|
|
||||||
Every check in the run happens even after an earlier one fails; the script exits non-zero if any did, and writes
|
|
||||||
`.test/check_profiles/logs/<check>.log` plus, on failure, `.test/check_profiles/pr_comment.md` — the same
|
|
||||||
report CI posts on the PR. A stale `.test/check_profiles/.lock` after a crash must be removed by hand.
|
|
||||||
|
|
||||||
## The five checks
|
|
||||||
|
|
||||||
| Check | Command it runs | Catches |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| `profile_tool` | `python3 scripts/orca_profile_tool.py check` | index coverage **both ways**, preset-name collisions, files `normalize`/`update-index` would still rewrite, duplicate JSON keys, filament `compatible_printers`, `filament_type` array, conflict keys, id length, **all `setting_id` and `filament_id` rules** |
|
|
||||||
| `validate_system` | `validator -p resources/profiles -l 2` | load errors, missing filament `compatible_printers`, dangling `inherits`/`compatible_*`, duplicate `filament_id` per printer |
|
|
||||||
| `validate_slice` | `validator -p … -s -l 2` | custom G-code expansion, unresolvable printer defaults |
|
|
||||||
| `validate_filament_subtypes` | `validator -p … -l 2 -f` | nothing extra — see below |
|
|
||||||
| `validate_custom` | `validator -p <tree+fixture> -l 2` | a shipped preset name that a past release offered no longer resolving |
|
|
||||||
|
|
||||||
**`-f` is a no-op.** It is declared `po::bool_switch()->default_value(true)`, so the duplicate-`filament_id`
|
|
||||||
check runs whether or not you pass it — `validate_system` already fails on duplicates. The binary's own
|
|
||||||
`--help` ("Off unless this flag is present") does not reflect that default.
|
|
||||||
|
|
||||||
### `validate_custom` — the backward-compatibility gate
|
|
||||||
|
|
||||||
Downloads one fixture archive per past release (v1.9.0 onwards) of *generated mock* user presets —
|
|
||||||
a `<vendor>_<preset>_orca_test` copy of every system preset that
|
|
||||||
release shipped, cut with the validator's own `-g 1` mode — unpacks each over a copy of the current tree
|
|
||||||
and loads it. Each entry holds only `inherits` plus a canned diff, so the one failure it adds over
|
|
||||||
`validate_system` is a shipped preset name disappearing. (The whole current tree sits under each fixture,
|
|
||||||
so every `validate_system` error fails it too.) This is what makes a rename or an
|
|
||||||
`instantiation` flip a CI failure rather than just a user complaint, and the reason `renamed_from` is
|
|
||||||
mandatory.
|
|
||||||
|
|
||||||
### `validate_slice`
|
|
||||||
|
|
||||||
Slices a two-colour cube on every instantiable printer in the tree, sequentially, forcing the prime tower.
|
|
||||||
It selects `default_print_profile` and the first `default_filament_profile`, then updates compatibility;
|
|
||||||
that update can select a different compatible preset. Confirm the intended defaults yourself rather
|
|
||||||
than treating a passing sweep as proof that those exact presets were sliced.
|
|
||||||
A printer fails if it cannot be selected, falls back to a Default preset, throws, produces no g-code, or
|
|
||||||
emits no `CP TOOLCHANGE START`. It cannot be scoped to a filament-only vendor
|
|
||||||
(`No instantiable printer presets found for vendor OrcaFilamentLibrary`); `check_profile.sh` records it
|
|
||||||
as SKIP for a vendor with no `machine/` folder.
|
|
||||||
|
|
||||||
## `orca_profile_tool.py check`
|
|
||||||
|
|
||||||
`check` is one subcommand of the tool that also owns
|
|
||||||
`generate-id`, `normalize`, `trim` and `update-index`; see [ids.md](ids.md) for the writing half.
|
|
||||||
|
|
||||||
| Per vendor | Catches |
|
|
||||||
| --- | --- |
|
|
||||||
| `check_preset_name_uniqueness` | two files in one bundle claiming one type + name — indexed or not |
|
|
||||||
| `check_index_coverage` | a file on disk that no `*_list` references (**an error, not a warning**) |
|
|
||||||
| `check_name_consistency` | an index entry whose `name` disagrees with the file, or whose `sub_path` is missing |
|
|
||||||
| `check_normalized` | a file `normalize` would rewrite, and an index `update-index` would rebuild |
|
|
||||||
| `check_filament_compatible_printers` | an instantiated non-library filament with no `compatible_printers` of its own |
|
|
||||||
| `check_conflict_keys` | `extruder_clearance_radius` alongside `extruder_clearance_max_radius` |
|
|
||||||
| `check_vector_type_keys` | a vector option written as a scalar (`"filament_type": "PLA"`) |
|
|
||||||
| `check_filament_id_length` | a declared `filament_id` longer than 8 characters |
|
|
||||||
| `check_machine_default_materials` | every `default_materials` / `default_filament_profile` name resolves |
|
|
||||||
| `check_obsolete_keys` | per-key warnings for ignored options; **filament files only** |
|
|
||||||
|
|
||||||
Tree-wide, **ignoring `--vendor` entirely**: `check_setting_id_uniqueness` and `check_filament_ids`. So a
|
|
||||||
vendor-scoped run can and does fail on another vendor's files — and it saves seconds, not minutes.
|
|
||||||
|
|
||||||
Unscoped, the per-vendor pass covers every bundle. The only exclusion is the stray `user/` directory
|
|
||||||
(see below); `OrcaFilamentLibrary` is held to the same rules as any vendor, its sole exemption being
|
|
||||||
that a library filament may leave `compatible_printers` empty — exactly what
|
|
||||||
`check_filament_compatible_printers` allows. `check_normalized` covers every bundle with an index.
|
|
||||||
|
|
||||||
Notes that matter:
|
|
||||||
|
|
||||||
- Exit codes: **0** clean, **1** errors found, **2** argparse misuse. Warnings never change the exit code.
|
|
||||||
- A nonexistent `--vendor` is a hard error — `[ERROR] unknown vendor "<V>" in <dir>`, exit 1.
|
|
||||||
- `--vendor ""` means all vendors; `check_profile.sh` relies on that. `--vendor` is repeatable.
|
|
||||||
- A **stray directory** under `resources/profiles/` still gets counted as a vendor by the per-vendor pass
|
|
||||||
and warned about (`No profiles found for vendor: <dir> at …/<dir>.json`, and the "Checked vendors" count
|
|
||||||
goes up by one). The one exception is `user/`, the validator's data dir, which an unscoped `check`
|
|
||||||
skips by name; `--vendor user` still checks and warns about it. Warnings never change the exit code.
|
|
||||||
`normalize`, `trim` and `update-index` ignore strays too — they define a bundle as *a directory with a
|
|
||||||
matching index file*.
|
|
||||||
- Each remedy is printed once for the whole run, not once per file, as a `[WARNING]` under the errors
|
|
||||||
("2 unreferenced file(s) above: delete them, or run … update-index"). Read those lines: they name the
|
|
||||||
command that fixes the batch.
|
|
||||||
- The trailing summary always suggests `normalize`. That is right for the shape errors and misleading for
|
|
||||||
everything else — an id error needs `generate-id`, a dangling `default_materials` needs a human.
|
|
||||||
- `resources/profiles/check_unused_setting_id.py` is a legacy BBL-only diagnostic, not part of
|
|
||||||
profile CI. Use `orca_profile_tool.py check` for current id validation.
|
|
||||||
|
|
||||||
### Obsolete-key diagnostics
|
|
||||||
|
|
||||||
`check` always reports per-key warnings for obsolete options in filament profiles.
|
|
||||||
The normalization check also rejects obsolete keys across preset types; `normalize` removes them.
|
|
||||||
|
|
||||||
### Default-material references
|
|
||||||
|
|
||||||
The materials check finds `default_materials` / `default_filament_profile` entries naming a preset
|
|
||||||
that does not exist. The three authoring errors it surfaces are `,` instead of `;`, wrong case
|
|
||||||
(`@system`), and a whole `;`-joined string stuffed into one array element.
|
|
||||||
|
|
||||||
### `normalize` and `update-index` are part of the check
|
|
||||||
|
|
||||||
`check` fails when either command would still change something, so they are not optional polish — the
|
|
||||||
file that gets reviewed has to be the file that ships. What `normalize` changes is narrow and fixed:
|
|
||||||
adds a missing `type`, deletes a `version` or `is_custom_defined` key from a *preset* file, deletes six
|
|
||||||
print-speed keys from filament profiles (`initial_layer_print_speed`, `outer_wall_speed`,
|
|
||||||
`inner_wall_speed`, `infill_speed`, `top_surface_speed`, `travel_speed`), deletes the
|
|
||||||
obsolete keys in `PrintConfigDef::handle_legacy`'s `ignore` set across preset types, resolves the
|
|
||||||
`extruder_clearance_*` conflict pair by keeping the larger, arrayifies five filament options besides
|
|
||||||
`filament_type`, and hoists `type`, `name`, `renamed_from`, `inherits`, `from`, `setting_id`,
|
|
||||||
`filament_id`, `instantiation` to the front. A file it changes is then rewritten whole — tab-indented,
|
|
||||||
LF, one trailing newline, keys reordered.
|
|
||||||
|
|
||||||
**Set `type` explicitly when authoring.** For a file in `machine/` without it, normalization guesses
|
|
||||||
`machine` only if its name contains `nozzle`, otherwise `machine_model`. That heuristic cannot
|
|
||||||
reliably classify shared machine bases or unusually named variants.
|
|
||||||
|
|
||||||
The Python obsolete-key set is checked against the C++ source by a unit test. Active options
|
|
||||||
and legacy aliases that the loader migrates (such as `extruder_type` and
|
|
||||||
`extruder_clearance_max_radius`) are preserved.
|
|
||||||
|
|
||||||
Two things it therefore does **not** enforce:
|
|
||||||
|
|
||||||
- **Formatting and key order on their own.** A file with none of those problems is skipped entirely, so
|
|
||||||
4-space indent, a missing trailing newline, and a file that leads with `compatible_printers` all pass
|
|
||||||
`check`. They stay latent until something else trips `normalize` and the whole file reformats inside an
|
|
||||||
unrelated diff. (`normalize --force` rewrites every file, which is not something to run on a shipped
|
|
||||||
bundle.)
|
|
||||||
- **A misspelled setting key.** `inital_layer_height` and `sparse_infill_densiti` pass `check` cleanly.
|
|
||||||
Verify new keys against `PrintConfig.cpp` and `PrintConfigDef::handle_legacy`.
|
|
||||||
|
|
||||||
## The validator binary
|
|
||||||
|
|
||||||
Built from `src/dev-utils/OrcaSlicer_profile_validator.cpp` (`-DORCA_TOOLS=ON`).
|
|
||||||
Both scripts find a local build under `build*/` — `check_profile.sh` tries Release, RelWithDebInfo, then
|
|
||||||
Debug, and `check_profile.ps1` adds MinSizeRel — else they download the nightly into
|
|
||||||
`.test/check_profiles/validator`. Pass `--download` / `-Download` to match CI exactly, since a stale
|
|
||||||
local build is used silently. Windows looks for `OrcaSlicer_profile_validator.exe`.
|
|
||||||
|
|
||||||
If your build lives somewhere else entirely, point at it with `--validator` / `-Validator`, or set
|
|
||||||
`ORCA_PROFILE_VALIDATOR` (`$env:ORCA_PROFILE_VALIDATOR` in PowerShell).
|
|
||||||
|
|
||||||
| Flag | Meaning |
|
|
||||||
| --- | --- |
|
|
||||||
| `-p <dir>` | profile tree (also becomes the data dir) |
|
|
||||||
| `-l <n>` | log level; CI uses 2 |
|
|
||||||
| `-v <Vendor>` | load only that vendor **plus** OrcaFilamentLibrary |
|
|
||||||
| `-s` | slice sweep |
|
|
||||||
| `-f` | no-op (see above) |
|
|
||||||
| `-g 1` | regenerate user-preset fixtures; takes a value, and wipes the user preset dir first |
|
|
||||||
|
|
||||||
On ARM64 Linux the nightly is x86-64 only — the script warns and downloads anyway, producing a binary
|
|
||||||
that will not run. Build it locally instead.
|
|
||||||
|
|
||||||
Running the validator directly uses the profile tree as its data directory and can create `user/`
|
|
||||||
there. Prefer the wrappers, which stash existing user presets and restore them afterward. After a
|
|
||||||
direct run, inspect `user/` and remove only empty directories created by that run; fixtures or
|
|
||||||
pre-existing user files may be present.
|
|
||||||
|
|
||||||
## Checking a copy of the tree
|
|
||||||
|
|
||||||
Use `--profiles DIR` on the Python tool and `-p DIR` on the validator. The wrappers' `--profiles` /
|
|
||||||
`-ProfilesDir` passes the tree to both, so one run validates a copy fully:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./scripts/check_profile.sh --profiles "<tree>"
|
|
||||||
```
|
|
||||||
|
|
||||||
On Windows use `scripts\check_profile.bat -ProfilesDir "<tree>"`.
|
|
||||||
|
|
||||||
## Testing in the app
|
|
||||||
|
|
||||||
Editing this checkout's `resources/profiles` does not update a separately installed application.
|
|
||||||
Test with a build using the edited resources and a bumped bundle version; the updater installs newer
|
|
||||||
bundles under `<data_dir>/system/`, and the preset cache also depends on the bundle version.
|
|
||||||
Use Help ▸ Show Configuration Folder to locate the active data directory:
|
|
||||||
|
|
||||||
| Platform | Default data directory |
|
|
||||||
| --- | --- |
|
|
||||||
| macOS | `~/Library/Application Support/OrcaSlicer` |
|
|
||||||
| Linux | `$XDG_CONFIG_HOME/OrcaSlicer`, or `~/.config/OrcaSlicer` when unset |
|
|
||||||
| Windows | `%APPDATA%\OrcaSlicer` |
|
|
||||||
|
|
||||||
A portable `data_dir` next to the executable takes precedence. Use a separate test configuration
|
|
||||||
for a clean-install check; preserve the normal configuration and user presets.
|
|
||||||
|
|
||||||
## Cross-platform paths
|
|
||||||
|
|
||||||
Match the exact case of each `sub_path` and asset filename; Linux filesystems commonly distinguish
|
|
||||||
case even when a macOS or Windows checkout does not. Preset-name references are case-sensitive
|
|
||||||
on every platform. Avoid Windows-invalid characters (`< > : " | ? *`), reserved device names
|
|
||||||
such as `CON` / `NUL` (including with extensions), and trailing spaces or dots in path components.
|
|
||||||
Keep stems tidy too, but a space immediately before `.json` is not a trailing path-component space.
|
|
||||||
|
|
||||||
## Error → remedy
|
|
||||||
|
|
||||||
| Message | Fix |
|
|
||||||
| --- | --- |
|
|
||||||
| `can not find inherits <parent> for <preset>` | parent missing, unregistered, or listed **after** the child |
|
|
||||||
| `can not find filament_id for <name>` | nothing in the chain declares one — run `generate-id` |
|
|
||||||
| `can not find parent <name> for config <user preset>!` | a shipped name disappeared — add `renamed_from` |
|
|
||||||
| `Missing instantiation attribute for <name>` | key absent **or** not the string `"true"`/`"false"` |
|
|
||||||
| `contains incorrect keys: <keys>, which were removed` | a key valid for a different preset type |
|
|
||||||
| `defines invalid printer variant "<v>"` | not in the model's `nozzle_diameter` list |
|
|
||||||
| `has printer_variant "<v>" that does not match its nozzle_diameter` | the set comparison in [machine-profiles.md](machine-profiles.md) |
|
|
||||||
| `references unknown compatible_printers "<p>"` | the printer was renamed or deleted; fix the reference |
|
|
||||||
| `references renamed compatible_printers "<old>" (now "<new>")` | in-tree references must name the current preset; `renamed_from` does not excuse them |
|
|
||||||
| `Filament preset "<f>" is missing compatible_printers setting` | non-library filaments need a non-empty list in their **own** file — the flattened-vs-own-key trap is in [filament-profiles.md](filament-profiles.md#compatible_printers) |
|
|
||||||
| `Ambiguous AMS filament match: N presets share filament_id "X" … printer "Y"` | make the lists disjoint, or fix an `inherits` pointing at another material's `@base` |
|
|
||||||
| `Layer height cannot exceed nozzle diameter.` / `Line width too small` | `Print::validate()` flow rules |
|
|
||||||
| `[ERROR] … no <V>.json list references it, so it never loads` | `update-index`, or delete the file |
|
|
||||||
| `[ERROR] … references it and it declares no profile type` | set the correct `type` explicitly, then `normalize` and `update-index` |
|
|
||||||
| `[ERROR] … normalize would <change>` / `<V>.json: update-index would rebuild <lists>` | run that command and commit the result |
|
|
||||||
| `[ERROR] <V> has N <type> profiles named "<name>"` | identify the intended preset and remove or rename the duplicate; use `trim --dry-run` only for deliberate unindexed-file cleanup |
|
|
||||||
| `[ERROR] … must not have a setting_id` / `is missing a setting_id` | `generate-id --setting-id` |
|
|
||||||
| `inherits filament_id "X" but its own triple … mints "Y"` | `generate-id` will **not** fix this — see [ids.md](ids.md) |
|
|
||||||
| `vendor <V>'s config version: <s> invalid` | the `version` string is not Semver-parseable |
|
|
||||||
| `[json.exception.type_error.302] type must be string` | locate the non-string value in the index or model; see [failure scopes](vendor-bundle.md#failure-modes-ranked-by-blast-radius) |
|
|
||||||
| `Printer "<p>" fell back to a default preset` | final process or filament selection is a generic Default preset; check named defaults, visibility and available compatible presets. An incompatible default may instead be replaced without this error |
|
|
||||||
| `Printer "<p>" sliced but the filament change never fired` | `change_filament_gcode` never expanded |
|
|
||||||
|
|
||||||
## CI
|
|
||||||
|
|
||||||
`.github/workflows/check_profiles.yml`, job **"Check profiles"**, on `pull_request` into `main` or
|
|
||||||
`release/*`, paths `resources/profiles/**`, `resources/printers/**`, `scripts/**` and the workflow itself.
|
|
||||||
There is no push trigger — a direct push to main runs no profile validation.
|
|
||||||
|
|
||||||
The job opens with `python3 -m unittest discover -s scripts/tests -t scripts`, the tool's own unit
|
|
||||||
tests. That step is deliberately **not** `continue-on-error`: a broken tool makes everything it then says
|
|
||||||
about the profiles worthless. Every check after it is `continue-on-error` with a final gate, so one run
|
|
||||||
reports all five results. On failure a second workflow posts or replaces a single PR comment marked
|
|
||||||
`<!-- profile-validation-comment -->`, with each failing log truncated to 30 KB; it deletes the comment
|
|
||||||
once the run is green.
|
|
||||||
|
|
||||||
The job name is also the required check for the delegated-merge bot, which lets a vendor maintainer
|
|
||||||
self-merge a `resources/profiles/<Their vendor>/` PR with no human review — so whatever CI does not check
|
|
||||||
is what ships unreviewed. Its denied patterns refuse `^scripts/` and any `.py`, so a PR that touches the
|
|
||||||
tooling always needs a maintainer.
|
|
||||||
@@ -1,175 +0,0 @@
|
|||||||
# The vendor bundle and the loader
|
|
||||||
|
|
||||||
A bundle is `resources/profiles/<Vendor>.json` (the index) plus `resources/profiles/<Vendor>/`.
|
|
||||||
The **vendor id is the filename stem**, not the `name` inside — several differ (`BBL.json` is named
|
|
||||||
"Bambulab"). Asset paths and the `setting_id` formula use the id; the `validate_custom` fixture prefix
|
|
||||||
uses the `name`.
|
|
||||||
|
|
||||||
## The index
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "Phrozen",
|
|
||||||
"version": "02.04.00.03",
|
|
||||||
"force_update": "0",
|
|
||||||
"description": "Phrozen configurations",
|
|
||||||
"machine_model_list": [ { "name": "...", "sub_path": "machine/....json" } ],
|
|
||||||
"machine_list": [ ... ],
|
|
||||||
"process_list": [ ... ],
|
|
||||||
"filament_list": [ ... ]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The loader reads `name`, `version`, `url` and the four `*_list` arrays.
|
|
||||||
`description` is only logged. `force_update` is read by `PresetUpdater`, never by the loader.
|
|
||||||
`sub_path` is relative to the **vendor folder**.
|
|
||||||
|
|
||||||
| List | Holds |
|
|
||||||
| --- | --- |
|
|
||||||
| `machine_model_list` | `machine_model` records (the printer product) |
|
|
||||||
| `machine_list` | printer variants **and** shared machine bases |
|
|
||||||
| `process_list` | selectable processes **and** shared process bases |
|
|
||||||
| `filament_list` | selectable filaments **and** shared filament bases |
|
|
||||||
|
|
||||||
### Three registration rules
|
|
||||||
|
|
||||||
1. **Everything is registered, bases included.** Every preset file on disk has exactly one entry in the
|
|
||||||
matching list, and no unindexed preset file is left in the tree.
|
|
||||||
2. **Parents before children.** `inherits` resolves against a per-kind map filled as the list is walked
|
|
||||||
(`configs.clear()` then process, filaments, printers). A parent listed after its child produces
|
|
||||||
`can not find inherits <parent> for <child>` and the bundle is discarded.
|
|
||||||
3. **The index entry's `name` must equal the `name` inside the sub_path file.** `check_name_consistency`
|
|
||||||
walks the index looking for the files; `check_index_coverage` walks the files looking for them in the
|
|
||||||
index. The `renamed_from` escape hatch `check_name_consistency`'s docstring promises is commented out.
|
|
||||||
|
|
||||||
All three are `check` errors now, and `update-index` writes an index that satisfies all three from the
|
|
||||||
files on disk — including the parents-first ordering, by topological sort. Hand-editing the index is
|
|
||||||
fine for a one-line addition, but the committed result must equal what `update-index` writes, because
|
|
||||||
`check` compares them.
|
|
||||||
|
|
||||||
The loader itself reports none of this: an unregistered file, or an entry with a typo'd key
|
|
||||||
(`"subpath"`), is silently dropped. (A typo'd `sub_path` is a `check` error naming the entry.)
|
|
||||||
|
|
||||||
`BBL/cli_config.json` and `BBL/filament/filaments_color_codes.json` are auxiliary data loaded by path,
|
|
||||||
not presets. The tool's `NON_PROFILE_FILES` excludes these basenames from preset maintenance.
|
|
||||||
|
|
||||||
## `version`
|
|
||||||
|
|
||||||
Parsed by a four-component Semver where the 4th is folded in as `patch = patch*100 + value`. Write it
|
|
||||||
zero-padded, `MM.mm.pp.bb`; a couple of bundles drop a component or the padding, but do not imitate them.
|
|
||||||
|
|
||||||
- **Bump the version for every bundle the PR touches.** `PresetUpdater` installs bundled resources
|
|
||||||
only when their version is newer than the installed version; the `.opc` preset cache is also
|
|
||||||
versioned. Nothing in profile CI checks the bump.
|
|
||||||
- **Keep the last component ≤ 99.** `02.04.00.100` and `02.04.01.00` both parse to `2.4.100`. A bundle
|
|
||||||
that reaches `.99` carries into the third component (`02.03.02.99` → `02.03.03.00`).
|
|
||||||
- An **absent** version is worse than a stale one: the validator still passes, but `Semver::valid()`
|
|
||||||
excludes `0.0.0`, so the vendor is dropped from the configuration wizard entirely and the preset cache
|
|
||||||
is disabled for it. An *unparseable* version is not silent — it throws and discards the whole bundle
|
|
||||||
(see the failure table below).
|
|
||||||
|
|
||||||
## Common preset keys
|
|
||||||
|
|
||||||
| Key | Value |
|
|
||||||
| --- | --- |
|
|
||||||
| `type` | `machine_model` / `machine` / `process` / `filament` |
|
|
||||||
| `name` | the preset name; the filename is *not* authoritative |
|
|
||||||
| `inherits` | the parent's exact `name` — no path, no `.json` |
|
|
||||||
| `instantiation` | the **string** `"true"` (selectable) or `"false"` (base) |
|
|
||||||
| `from` | `"system"` by convention; the vendor loader never reads it |
|
|
||||||
| `setting_id` | required on instantiated presets, forbidden on bases — generated |
|
|
||||||
| `renamed_from` | `;`-separated list of old names this preset supersedes |
|
|
||||||
|
|
||||||
These are config-preset keys; `machine_model` records have their own
|
|
||||||
[schema](machine-profiles.md#a-machine_model-is-not-a-config-preset). Keep `from` as `"system"`
|
|
||||||
for shipped presets. The vendor loader ignores it, but the CLI config-file loader accepts only
|
|
||||||
`system`, `user` or `User` and handles their inheritance differently.
|
|
||||||
|
|
||||||
`instantiation` is the one metadata key that is hard-gated: a missing key or any value other than the
|
|
||||||
strings `"true"`/`"false"` is an error (`Missing instantiation attribute for <name>`). A JSON boolean
|
|
||||||
`true` fails harder — it throws inside `load_from_json` and takes the **whole vendor bundle** down.
|
|
||||||
|
|
||||||
### `inherits`
|
|
||||||
|
|
||||||
Resolution is an exact-name lookup **within the same bundle**, plus one exception: filaments may inherit
|
|
||||||
from `OrcaFilamentLibrary`, which is loaded first and becomes the base bundle. Vendor-to-vendor
|
|
||||||
inheritance always fails. You can inherit from an instantiated preset as well as from a base; it is
|
|
||||||
common.
|
|
||||||
|
|
||||||
### `renamed_from`
|
|
||||||
|
|
||||||
One JSON string, `;`-separated for several old names.
|
|
||||||
|
|
||||||
- Write `"A;B"`, never `"A ; B"` — an unquoted item keeps its trailing space and can never match.
|
|
||||||
- When `renamed_from` is **absent** and the name contains `@`, the loader auto-adds the `@`-removed form
|
|
||||||
(`X @Y` → `X Y`) as a rename alias. Declaring an explicit `renamed_from` **suppresses** that, so a
|
|
||||||
preset that needs both the `@`-removed form and a real old name must list both. No shipped profile
|
|
||||||
currently does, which means any preset that gained a `renamed_from` quietly lost its `X Y` alias.
|
|
||||||
- It rescues names stored **outside** the tree: user presets and 3MF projects. It does **not** rescue
|
|
||||||
in-tree `inherits` (exact lookup), it does **not** satisfy `check_name_consistency`, the validator
|
|
||||||
reports an in-tree reference that only resolves through it (`references renamed compatible_printers
|
|
||||||
"OLD" (now "NEW")`), and `machine_model` records never read it at all.
|
|
||||||
- Only one preset may claim a given old name — two that do is a counted error
|
|
||||||
(`… was marked as renamed from "Y" … as well`). But the redirect is **inert while a live preset still
|
|
||||||
carries that name**, and nothing checks *that*; Z-Bolt ships a folder of such dead entries.
|
|
||||||
|
|
||||||
## Failure modes, ranked by blast radius
|
|
||||||
|
|
||||||
| Scope | Cause |
|
|
||||||
| --- | --- |
|
|
||||||
| **All vendors, zero system profiles** | a non-string `version`, `name` or `url` at the top level of a vendor index (`"version": 2`), or non-string `nozzle_diameter` on a model — `nlohmann::type_error` escapes the per-vendor `std::runtime_error` catch |
|
|
||||||
| **The whole vendor bundle** | unparseable `version`; index JSON parse error; a `sub_path` file missing or unparseable; unresolvable `inherits`; duplicate preset name within the vendor; empty/unknown `printer_model` or `printer_variant`; a filament resolving no `filament_id` |
|
|
||||||
| **One preset** | `instantiation` missing or a wrong string; keys belonging to another preset type (`contains incorrect keys: …, which were removed`); a non-string inside a `*_list` entry (`invalid value type for <key>`) |
|
|
||||||
| **Logged, not counted** | a raw JSON number in a preset — `invalid json type for <key>`, the value is dropped and the exit code stays 0 |
|
|
||||||
| **Nothing reported by the loader** | unregistered file; misspelled setting key; missing bed/hotend asset. Only the first of those is a `check` error; the other two reach users |
|
|
||||||
|
|
||||||
Deleting a file the index still lists surfaces as a *parse error* on line 1, not "file not found" — the
|
|
||||||
loader `ifstream`s the missing path and nlohmann reports `unexpected end of input`.
|
|
||||||
|
|
||||||
Preset names are a **single global namespace across every vendor**: a duplicate within one vendor is a
|
|
||||||
hard bundle failure, a duplicate across vendors is reported as `Found duplicated preset: <name> in
|
|
||||||
vendor: <vendor>` and still counts as an error. `check_preset_name_uniqueness` catches the within-bundle
|
|
||||||
case earlier and more precisely — including an *unindexed* twin, which is one `sub_path` edit away from
|
|
||||||
silently becoming the parent every child resolves to (`std::map::emplace` keeps the first insertion, so
|
|
||||||
index order decides). Base names, by contrast, repeat across bundles by design: `fdm_process_common`
|
|
||||||
exists in nearly all of them.
|
|
||||||
|
|
||||||
## Starting a whole new vendor bundle
|
|
||||||
|
|
||||||
Nothing generates one; copy the smallest bundle that resembles the hardware. **`Voxelab` or `M3D`** are
|
|
||||||
the minimal shape — a shared machine base, the model, one variant, a shared process base, two
|
|
||||||
processes, and an empty `filament_list` that takes the library generics. Do *not* start from `Phrozen`:
|
|
||||||
it carries local `fdm_filament_*` copies that have drifted from the library, and a filament preset that
|
|
||||||
restates most of its parent — the style this skill advises against.
|
|
||||||
|
|
||||||
Write the machine files **last**, so you only visit them once:
|
|
||||||
|
|
||||||
1. **Choose the names first** — model, variant(s), process(es). Everything else references them.
|
|
||||||
2. `resources/profiles/<Vendor>.json`: `name`, `version` (`01.00.00.00`), `force_update: "0"`,
|
|
||||||
`description`, and all four `*_list` arrays (an empty `filament_list` is fine).
|
|
||||||
3. The shared bases — `<Vendor>/machine/fdm_machine_common.json` and
|
|
||||||
`<Vendor>/process/fdm_process_common.json`, both `"instantiation": "false"` with no `setting_id`.
|
|
||||||
For a Klipper printer add your own `<Vendor>/machine/fdm_klipper_common.json` inheriting the machine
|
|
||||||
base; there is no shared one, because a `machine` preset can only inherit inside its own bundle.
|
|
||||||
4. One selectable process per variant, each naming its variant in `compatible_printers`.
|
|
||||||
5. Bed assets and `<Model>_cover.png`, all directly in `<Vendor>/`. None of them is needed for the
|
|
||||||
bundle to load, and nothing in CI checks them — but the bed files are inert unless the `machine_model`
|
|
||||||
names them in `bed_model` / `bed_texture`, and the cover is found by convention as
|
|
||||||
`<the name you gave the model in machine_model_list>_cover.png`.
|
|
||||||
6. The `machine_model` record and the `machine` variants, now that every value they reference exists —
|
|
||||||
the minimum key sets and the `default_*` shapes are in
|
|
||||||
[machine-profiles.md](machine-profiles.md#the-machine-variant).
|
|
||||||
7. Run the tool and validate — follow
|
|
||||||
[Creating or modifying a profile](../SKILL.md#creating-or-modifying-a-profile). `generate-id` is not
|
|
||||||
optional for a new bundle: the validator loads presets that have no `setting_id`, but `check` fails
|
|
||||||
every one of them. `update-index` will fill the four `*_list` arrays for you once the files exist, so
|
|
||||||
step 2 only needs the bundle metadata to be right.
|
|
||||||
|
|
||||||
## `resources/profiles_template/`
|
|
||||||
|
|
||||||
A separate tree (`Template.json` + `Template/`) holding filament and process templates. It is **not** a
|
|
||||||
scaffold for shipped profiles — `CreatePresetsDialog.cpp` reads it for the in-app "create a custom
|
|
||||||
printer/filament" wizard, so editing it changes what users get when they create a custom preset.
|
|
||||||
`check_profile.sh`'s validator checks default to `resources/profiles` (redirectable with `-p`), and so
|
|
||||||
does `orca_profile_tool.py` (redirectable with `--profiles`);
|
|
||||||
neither covers this tree.
|
|
||||||
@@ -1,12 +1,2 @@
|
|||||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
# Shell scripts are run by Git Bash on Windows CI, which cannot read a script
|
|
||||||
# with CRLF line endings: it fails on the first line. Windows checkouts default
|
|
||||||
# to core.autocrlf=true, so keep these LF whatever the platform.
|
|
||||||
*.sh text eol=lf
|
|
||||||
|
|
||||||
# Batch files are read by cmd.exe, which tracks a byte offset into the file to
|
|
||||||
# resume after `call :label`. With LF endings that offset can land wrong and the
|
|
||||||
# label lookup fails, so keep these CRLF whatever the platform.
|
|
||||||
*.bat text eol=crlf
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
name: 🐞 Bug Report
|
name: 🐞 Bug Report
|
||||||
description: Something behaves incorrectly while Orca Slicer keeps running
|
description: File a bug report
|
||||||
labels: ["bug"]
|
labels: ["bug"]
|
||||||
body:
|
body:
|
||||||
- type: markdown
|
- type: markdown
|
||||||
@@ -10,8 +10,6 @@ body:
|
|||||||
Please note that this is not the place to make feature requests or ask for help.
|
Please note that this is not the place to make feature requests or ask for help.
|
||||||
For this, please use the [Feature request](https://github.com/OrcaSlicer/OrcaSlicer/issues/new?assignees=&labels=&projects=&template=feature_request.yml) issue type or you can discuss your idea on our [Discord server](https://discord.gg/P4VE9UY9gJ) with others.
|
For this, please use the [Feature request](https://github.com/OrcaSlicer/OrcaSlicer/issues/new?assignees=&labels=&projects=&template=feature_request.yml) issue type or you can discuss your idea on our [Discord server](https://discord.gg/P4VE9UY9gJ) with others.
|
||||||
|
|
||||||
If Orca Slicer closes on its own, freezes or stops responding, please use the [Crash report](https://github.com/OrcaSlicer/OrcaSlicer/issues/new?assignees=&labels=&projects=&template=crash_report.yml) form instead. It asks for the logs a crash needs.
|
|
||||||
|
|
||||||
Before filing, please check if the issue already exists (either open or closed) by using the search bar on the issues page. If it does, comment there. Even if it's closed, we can reopen it based on your comment.
|
Before filing, please check if the issue already exists (either open or closed) by using the search bar on the issues page. If it does, comment there. Even if it's closed, we can reopen it based on your comment.
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
attributes:
|
attributes:
|
||||||
@@ -34,22 +32,14 @@ body:
|
|||||||
attributes:
|
attributes:
|
||||||
label: OrcaSlicer Version
|
label: OrcaSlicer Version
|
||||||
description: Which version of Orca Slicer are you running? You can see the full version in `Help` -> `About Orca Slicer`.
|
description: Which version of Orca Slicer are you running? You can see the full version in `Help` -> `About Orca Slicer`.
|
||||||
placeholder: e.g. 2.5.0
|
placeholder: e.g. 1.9.0
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: input
|
|
||||||
id: working_version
|
|
||||||
attributes:
|
|
||||||
label: Regression compared to a previous version
|
|
||||||
description: Did it work in a previous version?
|
|
||||||
placeholder: e.g. 2.3.2
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
id: os_type
|
id: os_type
|
||||||
attributes:
|
attributes:
|
||||||
label: "Operating System (OS)"
|
label: "Operating System (OS)"
|
||||||
description: "What OSes are you experiencing issues on?"
|
description: "What OSes are you are experiencing issues on?"
|
||||||
multiple: true
|
multiple: true
|
||||||
options:
|
options:
|
||||||
- Linux
|
- Linux
|
||||||
@@ -88,7 +78,7 @@ body:
|
|||||||
id: reproduce_steps
|
id: reproduce_steps
|
||||||
attributes:
|
attributes:
|
||||||
label: How to reproduce
|
label: How to reproduce
|
||||||
description: Please describe the detailed steps to reproduce this issue
|
description: Please described the detailed steps to reproduce this issue
|
||||||
placeholder: |
|
placeholder: |
|
||||||
1. Go to '...'
|
1. Go to '...'
|
||||||
2. Click on '...'
|
2. Click on '...'
|
||||||
@@ -110,23 +100,28 @@ body:
|
|||||||
description: What should happen after the above steps?
|
description: What should happen after the above steps?
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: markdown
|
||||||
|
id: file_required
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Please be sure to add the following files:
|
||||||
|
* Please upload a ZIP archive containing the **project file** used when the problem arise. Please export it just before or after the problem occurs. Even if you did nothing and/or there is no object, export it! (We need the configurations in project file).
|
||||||
|
You can export the project file from the application menu in `File`->`Save project as...`, then zip it
|
||||||
|
* A **log file** for crashes and similar issues.
|
||||||
|
You can find your log file here:
|
||||||
|
Windows: `%APPDATA%\OrcaSlicer\log` or usually `C:\Users\<your username>\AppData\Roaming\OrcaSlicer\log`
|
||||||
|
MacOS: `$HOME/Library/Application Support/OrcaSlicer/log`
|
||||||
|
Linux: `$HOME/.config/OrcaSlicer/log`
|
||||||
|
If Orca Slicer still starts, you can also reach this directory from the application menu in `Help` -> `Show Configuration Folder`
|
||||||
|
You can zip the log directory, or just select the newest logs when this issue happens, and zip them
|
||||||
- type: textarea
|
- type: textarea
|
||||||
id: file_uploads
|
id: file_uploads
|
||||||
attributes:
|
attributes:
|
||||||
label: Project file & Debug log uploads
|
label: Project file & Debug log uploads
|
||||||
description: |
|
description: Drop the project file and debug log here
|
||||||
Attach the files with the **Paste, drop, or click to add files** control directly underneath this box. Zip anything that is not a `.log`, `.txt` or image, since GitHub rejects other file types, and keep each file under 25 MB.
|
|
||||||
|
|
||||||
* The **project file** used when the problem happened, zipped. Export it just before or after the problem occurs. Even if you did nothing and there is no object on the plate, export it, since we need the configuration it carries. `File` -> `Save project as...`
|
|
||||||
* The **log folder**, zipped. `Help` -> `Show Configuration Folder` opens it, or find it at:
|
|
||||||
* Windows: `%APPDATA%\OrcaSlicer\log`, usually `C:\Users\<you>\AppData\Roaming\OrcaSlicer\log`
|
|
||||||
* macOS: `$HOME/Library/Application Support/OrcaSlicer/log`
|
|
||||||
* Linux: `$HOME/.config/OrcaSlicer/log`
|
|
||||||
* Flatpak: `$HOME/.var/app/com.orcaslicer.OrcaSlicer/config/OrcaSlicer/log`
|
|
||||||
* If the zip comes out over 25 MB, attach the newest logs from that folder on their own instead.
|
|
||||||
placeholder: |
|
placeholder: |
|
||||||
Zipped project file
|
Project File: `File` -> `Save project as...` then zip it & drop it here
|
||||||
Zipped log folder
|
Log File: `Help` -> `Show Configuration Folder`, then zip the log directory, or just select the newest logs in `log` when this issue happens and zip them, then drop the zip file here
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
- type: checkboxes
|
||||||
@@ -141,5 +136,7 @@ body:
|
|||||||
label: Anything else?
|
label: Anything else?
|
||||||
description: |
|
description: |
|
||||||
Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
||||||
|
|
||||||
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
validations:
|
validations:
|
||||||
required: false
|
required: false
|
||||||
|
|||||||
@@ -1,183 +0,0 @@
|
|||||||
name: 💥 Crash Report
|
|
||||||
description: Orca Slicer closes on its own, freezes or stops responding
|
|
||||||
labels: ["crash"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
**Thank you for taking the time to report a crash.**
|
|
||||||
|
|
||||||
Use this form when Orca Slicer closes on its own, freezes, or stops responding.
|
|
||||||
If the application stays open and only produces a wrong result, please use the [Bug report](https://github.com/OrcaSlicer/OrcaSlicer/issues/new?assignees=&labels=&projects=&template=bug_report.yml) form instead.
|
|
||||||
A printer whose toolhead collides with the print is also a bug report rather than a crash, since the application itself did not stop.
|
|
||||||
|
|
||||||
Before filing, please check if the issue already exists (either open or closed) by using the search bar on the issues page. If it does, comment there. Even if it's closed, we can reopen it based on your comment.
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is this crash reproducible in the latest nightly build?
|
|
||||||
description: >
|
|
||||||
Please verify this crash still happens in the latest nightly build first. It may already be fixed there:
|
|
||||||
[Nightly builds](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds).
|
|
||||||
options:
|
|
||||||
- label: I have checked the latest nightly build and the crash is still reproducible
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Is there an existing issue for this crash?
|
|
||||||
description: Please search to see if an issue already exists for the crash you encountered.
|
|
||||||
options:
|
|
||||||
- label: I have searched the existing issues
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: OrcaSlicer Version
|
|
||||||
description: Which version of Orca Slicer are you running? You can see the full version in `Help` -> `About Orca Slicer`.
|
|
||||||
placeholder: e.g. 2.5.0
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: working_version
|
|
||||||
attributes:
|
|
||||||
label: Regression compared to a previous version
|
|
||||||
description: Did it work in a previous version?
|
|
||||||
placeholder: e.g. 2.3.2
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: dropdown
|
|
||||||
id: os_type
|
|
||||||
attributes:
|
|
||||||
label: "Operating System (OS)"
|
|
||||||
description: "What OSes are you seeing the crash on?"
|
|
||||||
multiple: true
|
|
||||||
options:
|
|
||||||
- Linux
|
|
||||||
- macOS
|
|
||||||
- Windows
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: os_version
|
|
||||||
attributes:
|
|
||||||
label: "OS Version"
|
|
||||||
description: "What OS version does this relate to?"
|
|
||||||
placeholder: "i.e. OS: Windows 7/8/10/11 ..., Ubuntu 22.04/Fedora 36 ..., macOS 10.15/11.1/12.3 ..."
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
id: printer
|
|
||||||
attributes:
|
|
||||||
label: Printer
|
|
||||||
description: Which printer was selected
|
|
||||||
placeholder: Voron 2.4/VzBot/Prusa MK4/Bambu Lab X1 series/Bambu Lab P1P/...
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: crash_moment
|
|
||||||
attributes:
|
|
||||||
label: When does the crash happen?
|
|
||||||
description: Pick the point where Orca Slicer stops working.
|
|
||||||
options:
|
|
||||||
- Not sure
|
|
||||||
- On startup, before the main window appears
|
|
||||||
- When opening or importing a project or model
|
|
||||||
- While changing printer, filament or process settings
|
|
||||||
- While slicing
|
|
||||||
- In the 3D view, Preview or Assembly view
|
|
||||||
- When exporting G-code or sending a print to the printer
|
|
||||||
- On the Device tab, or connecting to a printer (camera, sync, login)
|
|
||||||
- While using a specific tool, dialog or calibration
|
|
||||||
- After resuming from sleep or changing monitors
|
|
||||||
- When closing the application
|
|
||||||
- No clear pattern
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: crash_frequency
|
|
||||||
attributes:
|
|
||||||
label: How often does it happen?
|
|
||||||
options:
|
|
||||||
- Not sure
|
|
||||||
- Every time
|
|
||||||
- Often, but not every time
|
|
||||||
- Rarely
|
|
||||||
- It only happened once
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
id: fresh_config
|
|
||||||
attributes:
|
|
||||||
label: Does it still crash with a fresh configuration?
|
|
||||||
description: >
|
|
||||||
Close Orca Slicer and rename your configuration folder (`%APPDATA%\OrcaSlicer` on Windows,
|
|
||||||
`$HOME/Library/Application Support/OrcaSlicer` on macOS, `$HOME/.config/OrcaSlicer` on Linux),
|
|
||||||
then start it again. Renaming keeps your settings, so you can put the folder back afterwards.
|
|
||||||
options:
|
|
||||||
- I have not tried this
|
|
||||||
- Yes, it still crashes
|
|
||||||
- No, the crash goes away
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: reproduce_steps
|
|
||||||
attributes:
|
|
||||||
label: How to reproduce
|
|
||||||
description: Please describe the detailed steps that lead to the crash.
|
|
||||||
placeholder: |
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '...'
|
|
||||||
3. Scroll down to '...'
|
|
||||||
4. Orca Slicer closes
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
id: system_info
|
|
||||||
attributes:
|
|
||||||
label: Additional system information
|
|
||||||
description: >
|
|
||||||
Display card and driver version are worth adding for crashes on startup or in the 3D view.
|
|
||||||
CPU and memory are worth adding for crashes while slicing.
|
|
||||||
placeholder: |
|
|
||||||
CPU: 11th gen Intel r core tm i7-1185g7/AMD Ryzen 7 6800h/...
|
|
||||||
Memory: 32/16 GB...
|
|
||||||
Display Card: NVIDIA Quadro P400/...
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
- type: textarea
|
|
||||||
id: file_uploads
|
|
||||||
attributes:
|
|
||||||
label: Project file, logs and crash report uploads
|
|
||||||
description: |
|
|
||||||
A crash report without logs usually cannot be acted on. Attach the files with the **Paste, drop, or click to add files** control directly underneath this box. Zip anything that is not a `.log`, `.txt` or image, since GitHub rejects other file types, and keep each file under 25 MB.
|
|
||||||
|
|
||||||
* The **project file** used when the crash happened, zipped. Export it just before or after the crash, even if the plate is empty, since we need the configuration it carries. `File` -> `Save project as...`
|
|
||||||
* The whole **log folder**, zipped rather than single files picked out of it. `Help` -> `Show Configuration Folder` opens it, or find it at:
|
|
||||||
* Windows: `%APPDATA%\OrcaSlicer\log`, usually `C:\Users\<you>\AppData\Roaming\OrcaSlicer\log`
|
|
||||||
* macOS: `$HOME/Library/Application Support/OrcaSlicer/log`
|
|
||||||
* Linux: `$HOME/.config/OrcaSlicer/log`
|
|
||||||
* Flatpak: `$HOME/.var/app/com.orcaslicer.OrcaSlicer/config/OrcaSlicer/log`
|
|
||||||
* On Windows the crash itself is written to a separate `crash_*.log` in there, and that is the file we need most. If the zip comes out over 25 MB GitHub will refuse it, so attach the newest log and any `crash_*.log` on their own instead.
|
|
||||||
* The **operating system crash report**, on macOS and Linux, where Orca Slicer cannot write its own crash log. It is often the only record of where it died:
|
|
||||||
* macOS: Console.app -> Crash Reports, or `$HOME/Library/Logs/DiagnosticReports/`. The file starts with `OrcaSlicer` and ends in `.ips`. Zip it before attaching, GitHub does not accept `.ips` files.
|
|
||||||
* Linux: run `orca-slicer` from a terminal (Flatpak: `flatpak run com.orcaslicer.OrcaSlicer`) and paste everything it prints when it dies. On systemd systems `coredumpctl info orca-slicer` gives a backtrace.
|
|
||||||
placeholder: |
|
|
||||||
Zipped project file
|
|
||||||
Zipped log folder
|
|
||||||
Zipped macOS .ips crash report, or the terminal output on Linux
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
id: file_checklist
|
|
||||||
attributes:
|
|
||||||
label: Checklist of files to include
|
|
||||||
options:
|
|
||||||
- label: Log folder
|
|
||||||
- label: Project file
|
|
||||||
- label: Operating system crash report (macOS and Linux)
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Anything else?
|
|
||||||
description: |
|
|
||||||
Screenshots? References? Anything that will give us more context about the crash you are encountering!
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
@@ -15,7 +15,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Bun
|
- name: Setup Bun
|
||||||
uses: oven-sh/setup-bun@v2
|
uses: oven-sh/setup-bun@v2
|
||||||
|
|||||||
+51
-298
@@ -5,7 +5,6 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- release/*
|
- release/*
|
||||||
- belt-printer
|
|
||||||
paths:
|
paths:
|
||||||
- 'deps/**'
|
- 'deps/**'
|
||||||
- 'src/**'
|
- 'src/**'
|
||||||
@@ -14,13 +13,7 @@ on:
|
|||||||
- 'localization/**'
|
- 'localization/**'
|
||||||
- 'resources/**'
|
- 'resources/**'
|
||||||
- ".github/workflows/build_*.yml"
|
- ".github/workflows/build_*.yml"
|
||||||
- ".github/workflows/unit_tests*.yml"
|
|
||||||
- 'build_win.bat'
|
|
||||||
- 'scripts/test_build_win.ps1'
|
|
||||||
- 'scripts/build_preset_cache.*'
|
|
||||||
- 'scripts/flatpak/**'
|
- 'scripts/flatpak/**'
|
||||||
- 'scripts/msix/**'
|
|
||||||
- 'tests/**'
|
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
@@ -33,15 +26,10 @@ on:
|
|||||||
- '**/CMakeLists.txt'
|
- '**/CMakeLists.txt'
|
||||||
- 'version.inc'
|
- 'version.inc'
|
||||||
- ".github/workflows/build_*.yml"
|
- ".github/workflows/build_*.yml"
|
||||||
- ".github/workflows/unit_tests*.yml"
|
|
||||||
- 'build_linux.sh'
|
- 'build_linux.sh'
|
||||||
- 'build_win.bat'
|
- 'build_release_vs2022.bat'
|
||||||
- 'scripts/test_build_win.ps1'
|
|
||||||
- 'build_release_macos.sh'
|
- 'build_release_macos.sh'
|
||||||
- 'scripts/build_preset_cache.*'
|
|
||||||
- 'scripts/flatpak/**'
|
- 'scripts/flatpak/**'
|
||||||
- 'scripts/msix/**'
|
|
||||||
- 'tests/**'
|
|
||||||
|
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
@@ -60,57 +48,22 @@ concurrency:
|
|||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# build_win.bat ships a test suite. Run it before the Windows builds.
|
|
||||||
check_build_script:
|
|
||||||
name: Windows build script tests
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
lfs: 'false'
|
|
||||||
|
|
||||||
# Windows PowerShell rather than pwsh: the suite drives build_win.bat
|
|
||||||
# through cmd, and the two differ in how they quote native arguments.
|
|
||||||
- name: Run the build script test suite
|
|
||||||
shell: powershell
|
|
||||||
run: .\scripts\test_build_win.ps1
|
|
||||||
|
|
||||||
build_linux:
|
build_linux:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
# SELF_HOSTED skips arm64 (no arm self-hosted server). amd64's empty arch
|
|
||||||
# is load-bearing: it keeps the historical 'linux-clang' deps cache key and
|
|
||||||
# the unsuffixed asset names.
|
|
||||||
matrix:
|
|
||||||
include: ${{ fromJSON(vars.SELF_HOSTED
|
|
||||||
&& '[{"arch":"","os":"orca-lnx-server"}]'
|
|
||||||
|| '[{"arch":"","os":"ubuntu-24.04"},{"arch":"aarch64","os":"ubuntu-24.04-arm"}]') }}
|
|
||||||
# Don't run scheduled builds on forks:
|
# Don't run scheduled builds on forks:
|
||||||
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||||
uses: ./.github/workflows/build_check_cache.yml
|
uses: ./.github/workflows/build_check_cache.yml
|
||||||
with:
|
with:
|
||||||
os: ${{ matrix.os }}
|
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||||
arch: ${{ matrix.arch }}
|
|
||||||
build-deps-only: ${{ inputs.build-deps-only || false }}
|
build-deps-only: ${{ inputs.build-deps-only || false }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
build_windows:
|
build_windows:
|
||||||
name: Build Windows ${{ matrix.arch }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
# SELF_HOSTED skips arm64 (the self-hosted Windows server is x64-only).
|
|
||||||
matrix:
|
|
||||||
include: ${{ fromJSON(vars.SELF_HOSTED
|
|
||||||
&& '[{"arch":"x64","os":"orca-win-server","compiler":"clang"}]'
|
|
||||||
|| '[{"arch":"x64","os":"windows-latest","compiler":"clang"},{"arch":"arm64","os":"windows-11-vs2026-arm","compiler":"clang"}]') }}
|
|
||||||
needs: check_build_script
|
|
||||||
# Don't run scheduled builds on forks:
|
# Don't run scheduled builds on forks:
|
||||||
if: ${{ !cancelled() && needs.check_build_script.result == 'success' && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||||
uses: ./.github/workflows/build_check_cache.yml
|
uses: ./.github/workflows/build_check_cache.yml
|
||||||
with:
|
with:
|
||||||
os: ${{ matrix.os }}
|
os: ${{ vars.SELF_HOSTED && 'orca-win-server' || 'windows-latest' }}
|
||||||
arch: ${{ matrix.arch }}
|
|
||||||
compiler: ${{ matrix.compiler }}
|
|
||||||
build-deps-only: ${{ inputs.build-deps-only || false }}
|
build-deps-only: ${{ inputs.build-deps-only || false }}
|
||||||
force-build: ${{ github.event_name == 'schedule' }}
|
force-build: ${{ github.event_name == 'schedule' }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
@@ -140,97 +93,52 @@ jobs:
|
|||||||
arch: universal
|
arch: universal
|
||||||
macos-combine-only: true
|
macos-combine-only: true
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
# One test job per built arch, on the runner that built it.
|
unit_tests:
|
||||||
unit_tests_linux_x86_64:
|
name: Unit Tests
|
||||||
name: Linux x86_64
|
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||||
needs: build_linux
|
needs: build_linux
|
||||||
if: ${{ !cancelled() && success() }}
|
if: ${{ !cancelled() && success() }}
|
||||||
uses: ./.github/workflows/unit_tests.yml
|
|
||||||
with:
|
|
||||||
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
|
||||||
artifact: ${{ github.sha }}-tests-linux-x86_64
|
|
||||||
unit_tests_linux_aarch64:
|
|
||||||
name: Linux aarch64
|
|
||||||
needs: build_linux
|
|
||||||
if: ${{ !cancelled() && success() && !vars.SELF_HOSTED }}
|
|
||||||
uses: ./.github/workflows/unit_tests.yml
|
|
||||||
with:
|
|
||||||
os: ubuntu-24.04-arm
|
|
||||||
artifact: ${{ github.sha }}-tests-linux-aarch64
|
|
||||||
unit_tests_windows_x64:
|
|
||||||
name: Windows x64
|
|
||||||
needs: build_windows
|
|
||||||
if: ${{ !cancelled() && success() }}
|
|
||||||
uses: ./.github/workflows/unit_tests.yml
|
|
||||||
with:
|
|
||||||
os: ${{ vars.SELF_HOSTED && 'orca-win-server' || 'windows-latest' }}
|
|
||||||
artifact: ${{ github.sha }}-tests-windows-x64
|
|
||||||
unit_tests_windows_arm64:
|
|
||||||
name: Windows arm64
|
|
||||||
needs: build_windows
|
|
||||||
if: ${{ !cancelled() && success() && !vars.SELF_HOSTED }}
|
|
||||||
uses: ./.github/workflows/unit_tests.yml
|
|
||||||
with:
|
|
||||||
os: windows-11-vs2026-arm
|
|
||||||
artifact: ${{ github.sha }}-tests-windows-arm64
|
|
||||||
test-dir: build-arm64/tests
|
|
||||||
unit_tests_macos_arm64:
|
|
||||||
name: macOS arm64
|
|
||||||
needs: build_macos_arch
|
|
||||||
if: ${{ !cancelled() && success() }}
|
|
||||||
uses: ./.github/workflows/unit_tests.yml
|
|
||||||
with:
|
|
||||||
os: ${{ vars.SELF_HOSTED && 'orca-macos-arm64' || 'macos-14' }}
|
|
||||||
artifact: ${{ github.sha }}-tests-macos-arm64
|
|
||||||
test-dir: build/arm64/tests
|
|
||||||
# Slice a two-colour cube through every shipped printer so all custom g-code
|
|
||||||
# (change_filament_gcode, machine start/end, etc.) is expanded - catches
|
|
||||||
# slicing regressions the static profile checks and unit tests can't see.
|
|
||||||
# Profile-only PRs are covered by check_profiles.yml's nightly binary; this
|
|
||||||
# covers src/engine PRs with the PR-built binary.
|
|
||||||
slice_check_linux:
|
|
||||||
name: Slice check (Linux ${{ vars.SELF_HOSTED && 'x86_64' || 'aarch64' }})
|
|
||||||
needs: build_linux
|
|
||||||
if: ${{ !cancelled() && success() }}
|
|
||||||
# Follows whichever Linux leg built the validator.
|
|
||||||
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04-arm' }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
- name: Download profile validator
|
with:
|
||||||
|
sparse-checkout: |
|
||||||
|
.github
|
||||||
|
scripts
|
||||||
|
tests
|
||||||
|
- name: Apt-Install Dependencies
|
||||||
|
if: ${{ !vars.SELF_HOSTED }}
|
||||||
|
uses: ./.github/actions/apt-install-deps
|
||||||
|
- name: Restore Test Artifact
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v8
|
||||||
with:
|
with:
|
||||||
name: ${{ github.sha }}-profile-validator-linux-${{ vars.SELF_HOSTED && 'x86_64' || 'aarch64' }}
|
name: ${{ github.sha }}-tests
|
||||||
path: validator-bin
|
- uses: lukka/get-cmake@latest
|
||||||
- name: Validate slice (expand custom g-code)
|
with:
|
||||||
timeout-minutes: 60
|
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
||||||
|
useLocalCache: true # <--= Use the local cache (default is 'false').
|
||||||
|
useCloudCache: true
|
||||||
|
- name: Unpackage and Run Unit Tests
|
||||||
|
timeout-minutes: 20
|
||||||
run: |
|
run: |
|
||||||
chmod +x validator-bin/OrcaSlicer_profile_validator
|
tar -xvf build_tests.tar
|
||||||
./validator-bin/OrcaSlicer_profile_validator -p "${{ github.workspace }}/resources/profiles" -s -l 2
|
scripts/run_unit_tests.sh
|
||||||
publish_test_results:
|
- name: Upload Test Logs
|
||||||
name: Publish Test Results
|
uses: actions/upload-artifact@v7
|
||||||
needs: [unit_tests_linux_x86_64, unit_tests_linux_aarch64, unit_tests_windows_x64, unit_tests_windows_arm64, unit_tests_macos_arm64, unit_tests_flatpak_x86_64, unit_tests_flatpak_aarch64]
|
if: ${{ failure() }}
|
||||||
if: ${{ !cancelled() }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download Test Results
|
|
||||||
uses: actions/download-artifact@v8
|
|
||||||
with:
|
with:
|
||||||
pattern: test-results-*
|
name: unit-test-logs
|
||||||
path: test-results
|
path: build/tests/**/*.log
|
||||||
# Best-effort: a read-only token (e.g. fork PRs) can't write the check, so
|
|
||||||
# don't let a publish failure fail the run. The test jobs gate correctness.
|
|
||||||
- name: Publish Test Results
|
- name: Publish Test Results
|
||||||
continue-on-error: true
|
if: always()
|
||||||
uses: EnricoMi/publish-unit-test-result-action@v2
|
uses: EnricoMi/publish-unit-test-result-action@v2
|
||||||
with:
|
with:
|
||||||
files: "test-results/**/*.xml"
|
files: "ctest_results.xml"
|
||||||
- name: Delete Test Results
|
- name: Delete Test Artifact
|
||||||
if: success()
|
if: success()
|
||||||
uses: geekyeggo/delete-artifact@v6
|
uses: geekyeggo/delete-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: test-results-*
|
name: ${{ github.sha }}-tests
|
||||||
failOnError: false
|
|
||||||
flatpak:
|
flatpak:
|
||||||
name: "Flatpak"
|
name: "Flatpak"
|
||||||
container:
|
container:
|
||||||
@@ -258,14 +166,11 @@ jobs:
|
|||||||
date:
|
date:
|
||||||
ver:
|
ver:
|
||||||
ver_pure:
|
ver_pure:
|
||||||
# Belt-printer nightlies share the main nightly release but carry a `_belt`
|
|
||||||
# suffix so they never overwrite the main assets.
|
|
||||||
nightly_suffix: ${{ github.ref == 'refs/heads/belt-printer' && '_belt' || '' }}
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Remove unneeded stuff to free disk space"
|
- name: "Remove unneeded stuff to free disk space"
|
||||||
run:
|
run:
|
||||||
rm -rf /usr/local/lib/android/* /usr/share/dotnet/* /opt/ghc1/* "/usr/local/share/boost1/*" /opt/hostedtoolcache1/*
|
rm -rf /usr/local/lib/android/* /usr/share/dotnet/* /opt/ghc1/* "/usr/local/share/boost1/*" /opt/hostedtoolcache1/*
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v6
|
||||||
- name: Get the version and date
|
- name: Get the version and date
|
||||||
run: |
|
run: |
|
||||||
ver_pure=$(grep 'set(SoftFever_VERSION' version.inc | cut -d '"' -f2)
|
ver_pure=$(grep 'set(SoftFever_VERSION' version.inc | cut -d '"' -f2)
|
||||||
@@ -281,203 +186,51 @@ jobs:
|
|||||||
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||||
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
|
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
|
||||||
shell: bash
|
shell: bash
|
||||||
- name: Compute the flatpak-builder cache key
|
# Manage flatpak-builder cache externally so PRs restore but never upload
|
||||||
id: fp_cache_key
|
|
||||||
run: echo "key=flatpak-builder-${{ matrix.variant.arch }}-${{ hashFiles('deps/**', 'scripts/flatpak/com.orcaslicer.OrcaSlicer.yml', 'scripts/flatpak/make_deps_tar.sh') }}" >> "$GITHUB_OUTPUT"
|
|
||||||
shell: bash
|
|
||||||
# Manage flatpak-builder cache externally so PRs restore but never upload.
|
|
||||||
# The compiler cache under it is keyed per run below, so it is left out.
|
|
||||||
- name: Restore flatpak-builder cache
|
- name: Restore flatpak-builder cache
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
uses: actions/cache/restore@v6
|
uses: actions/cache/restore@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: .flatpak-builder
|
||||||
.flatpak-builder/*
|
key: flatpak-builder-${{ matrix.variant.arch }}-${{ github.event.pull_request.base.sha }}
|
||||||
!.flatpak-builder/ccache
|
|
||||||
key: ${{ steps.fp_cache_key.outputs.key }}
|
|
||||||
restore-keys: flatpak-builder-${{ matrix.variant.arch }}-
|
restore-keys: flatpak-builder-${{ matrix.variant.arch }}-
|
||||||
- name: Save/restore flatpak-builder cache
|
- name: Save/restore flatpak-builder cache
|
||||||
if: github.event_name != 'pull_request'
|
if: github.event_name != 'pull_request'
|
||||||
uses: actions/cache@v6
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: |
|
path: .flatpak-builder
|
||||||
.flatpak-builder/*
|
key: flatpak-builder-${{ matrix.variant.arch }}-${{ github.sha }}
|
||||||
!.flatpak-builder/ccache
|
|
||||||
key: ${{ steps.fp_cache_key.outputs.key }}
|
|
||||||
restore-keys: flatpak-builder-${{ matrix.variant.arch }}-
|
restore-keys: flatpak-builder-${{ matrix.variant.arch }}-
|
||||||
# Compiler cache for the OrcaSlicer module, as in build_orca.yml. Pull
|
|
||||||
# requests only restore it; every other run (main, release branches, the
|
|
||||||
# nightly, a dispatch) saves it. orca_deps stays on the state cache above.
|
|
||||||
- name: Name the compiler cache leg
|
|
||||||
run: |
|
|
||||||
leg="Flatpak-${{ matrix.variant.arch }}"
|
|
||||||
echo "CCACHE_LEG=$leg" >> "$GITHUB_ENV"
|
|
||||||
echo "CCACHE_ENTRY=ccache-$leg-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_ENV"
|
|
||||||
shell: bash
|
|
||||||
- name: Restore compiler cache
|
|
||||||
id: ccache_restore
|
|
||||||
uses: actions/cache/restore@v6
|
|
||||||
with:
|
|
||||||
path: .flatpak-builder/ccache
|
|
||||||
key: ${{ env.CCACHE_ENTRY }}
|
|
||||||
restore-keys: ccache-${{ env.CCACHE_LEG }}-
|
|
||||||
- name: Disable debug info for faster CI builds
|
- name: Disable debug info for faster CI builds
|
||||||
run: |
|
run: |
|
||||||
sed -i '/^build-options:/a\ no-debuginfo: true\n strip: true' \
|
sed -i '/^build-options:/a\ no-debuginfo: true\n strip: true' \
|
||||||
scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
||||||
shell: bash
|
shell: bash
|
||||||
# flatpak-builder reuses a module from its cache when the definition and
|
- name: Inject git commit hash into Flatpak manifest
|
||||||
# sources are unchanged, so a re-run of the same commit would skip the
|
|
||||||
# OrcaSlicer module and ship no test asset. A per-run value in that module's
|
|
||||||
# env keeps it rebuilding; orca_deps stays cached, and the compiler cache
|
|
||||||
# still serves the rebuild.
|
|
||||||
- name: Inject commit hash and flatpak-builder cache buster into Flatpak manifest
|
|
||||||
env:
|
|
||||||
flatpak_builder_cache_buster: ${{ github.run_id }}-${{ github.run_attempt }}
|
|
||||||
run: |
|
run: |
|
||||||
sed -i "/name: OrcaSlicer/{n;s|buildsystem: simple|buildsystem: simple\n build-options:\n env:\n flatpak_builder_cache_buster: \"$flatpak_builder_cache_buster\"\n git_commit_hash: \"$git_commit_hash\"|}" \
|
sed -i "/name: OrcaSlicer/{n;s|buildsystem: simple|buildsystem: simple\n build-options:\n env:\n git_commit_hash: \"$git_commit_hash\"|}" \
|
||||||
scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
||||||
shell: bash
|
shell: bash
|
||||||
# flatpak-builder's --ccache only wraps cc and gcc, and the manifest builds
|
|
||||||
# with clang, so CMake's launcher runs ccache instead; --ccache is still what
|
|
||||||
# mounts the cache directory into the sandbox. The settings go into that
|
|
||||||
# directory's own config file, which the sandbox reads too.
|
|
||||||
- name: Enable compiler cache
|
|
||||||
run: |
|
|
||||||
printf ' %s\n' \
|
|
||||||
'CMAKE_C_COMPILER_LAUNCHER: ccache' \
|
|
||||||
'CMAKE_CXX_COMPILER_LAUNCHER: ccache' > "$RUNNER_TEMP/ccache-env.yml"
|
|
||||||
sed -i "/^ git_commit_hash: /r $RUNNER_TEMP/ccache-env.yml" \
|
|
||||||
scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
|
||||||
grep -q '^ CMAKE_CXX_COMPILER_LAUNCHER: ccache$' scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
|
||||||
mkdir -p .flatpak-builder/ccache
|
|
||||||
export CCACHE_DIR=$PWD/.flatpak-builder/ccache
|
|
||||||
ccache --set-config=max_size=3G
|
|
||||||
# The compiler is reinstalled every run, so its mtime means nothing.
|
|
||||||
ccache --set-config=compiler_check=content
|
|
||||||
# Headers a fresh checkout has just written, the few files that use
|
|
||||||
# __DATE__ or __TIME__, and the precompiled header, whose macros ccache
|
|
||||||
# cannot see.
|
|
||||||
ccache --set-config=sloppiness=pch_defines,time_macros,include_file_mtime,include_file_ctime
|
|
||||||
# Hash the includes the compiler reports instead of preprocessing every
|
|
||||||
# miss before compiling it.
|
|
||||||
ccache --set-config=depend_mode=true
|
|
||||||
# The restored directory carries the previous run's counters.
|
|
||||||
ccache -z
|
|
||||||
shell: bash
|
|
||||||
- name: Check the manifest keeps orca_deps cacheable
|
|
||||||
run: ./scripts/flatpak/check_manifest_cacheable.sh
|
|
||||||
shell: bash
|
|
||||||
- name: Pack deps/ for the Flatpak manifest
|
|
||||||
run: ./scripts/flatpak/make_deps_tar.sh
|
|
||||||
shell: bash
|
|
||||||
- uses: flatpak/flatpak-github-actions/flatpak-builder@master
|
- uses: flatpak/flatpak-github-actions/flatpak-builder@master
|
||||||
with:
|
with:
|
||||||
bundle: OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
|
bundle: OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
|
||||||
manifest-path: scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
manifest-path: scripts/flatpak/com.orcaslicer.OrcaSlicer.yml
|
||||||
# cache only turns on flatpak-builder --ccache; the caching itself is above.
|
cache: false
|
||||||
cache: true
|
|
||||||
restore-cache: false
|
|
||||||
save-cache: false
|
|
||||||
arch: ${{ matrix.variant.arch }}
|
arch: ${{ matrix.variant.arch }}
|
||||||
upload-artifact: false
|
upload-artifact: false
|
||||||
# run-tests fires the module's build-only test-commands; keep-build-dirs
|
|
||||||
# retains the binaries for the packaging step below.
|
|
||||||
run-tests: true
|
|
||||||
keep-build-dirs: true
|
|
||||||
# The build has just touched everything it can use, so an object untouched
|
|
||||||
# for a week is dead, usually orphaned by a flag change.
|
|
||||||
- name: Compiler cache statistics
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
export CCACHE_DIR=$PWD/.flatpak-builder/ccache
|
|
||||||
ccache --evict-older-than 7d
|
|
||||||
ccache -s -v || ccache -s
|
|
||||||
shell: bash
|
|
||||||
# Save the new entry first, then drop the older ones for this leg on this
|
|
||||||
# ref, so a failed save leaves the previous entry in place. A cancelled or
|
|
||||||
# failed build saves too, since what it compiled is still valid; a restore
|
|
||||||
# that did not finish does not, since the directory may be a truncated copy.
|
|
||||||
- name: Save compiler cache
|
|
||||||
id: ccache_save
|
|
||||||
if: ${{ always() && steps.ccache_restore.outcome == 'success' && github.event_name != 'pull_request' }}
|
|
||||||
uses: actions/cache/save@v6
|
|
||||||
with:
|
|
||||||
path: .flatpak-builder/ccache
|
|
||||||
key: ${{ env.CCACHE_ENTRY }}
|
|
||||||
- name: Drop older compiler cache entries
|
|
||||||
if: ${{ always() && steps.ccache_save.outcome == 'success' }}
|
|
||||||
# The container has no gh, so this is the list and delete over the REST API.
|
|
||||||
# Older means a lower run id, so two runs finishing close together keep
|
|
||||||
# the newer entry whichever of them cleans up last.
|
|
||||||
continue-on-error: true
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
run: |
|
|
||||||
api="$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/caches"
|
|
||||||
curl -sSf -H "Authorization: Bearer $GH_TOKEN" \
|
|
||||||
"$api?ref=$GITHUB_REF&key=ccache-$CCACHE_LEG-&per_page=100" \
|
|
||||||
| jq -r --arg prefix "ccache-$CCACHE_LEG-" --argjson run "$GITHUB_RUN_ID" \
|
|
||||||
'.actions_caches[] | select((.key | ltrimstr($prefix) | split("-")[0] | tonumber?) < $run) | .id' \
|
|
||||||
| while read -r id; do
|
|
||||||
curl -sSf -X DELETE -H "Authorization: Bearer $GH_TOKEN" "$api/$id"
|
|
||||||
done
|
|
||||||
shell: bash
|
|
||||||
- name: Upload artifacts Flatpak
|
- name: Upload artifacts Flatpak
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
|
name: OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
|
||||||
path: '/__w/OrcaSlicer/OrcaSlicer/OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak'
|
path: '/__w/OrcaSlicer/OrcaSlicer/OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak'
|
||||||
- name: Deploy Flatpak to nightly release
|
- name: Deploy Flatpak to nightly release
|
||||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/belt-printer')
|
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main'
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
release_id: 137995723
|
release_id: 137995723
|
||||||
asset_path: /__w/OrcaSlicer/OrcaSlicer/OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
|
asset_path: /__w/OrcaSlicer/OrcaSlicer/OrcaSlicer-Linux-flatpak_${{ env.ver }}_${{ matrix.variant.arch }}.flatpak
|
||||||
asset_name: OrcaSlicer-Linux-flatpak_nightly${{ env.nightly_suffix }}_${{ matrix.variant.arch }}.flatpak
|
asset_name: OrcaSlicer-Linux-flatpak_nightly_${{ matrix.variant.arch }}.flatpak
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
||||||
# The asset is /app (the exes link it at runtime) plus the build tree
|
|
||||||
# slimmed to what ctest needs.
|
|
||||||
- name: Package flatpak test asset
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
d=$(ls -d .flatpak-builder/build/OrcaSlicer-* | tail -1)
|
|
||||||
find "$d/build_flatpak" -mindepth 1 -maxdepth 1 ! -name tests -exec rm -rf {} +
|
|
||||||
# Strip debug info (the SDK builds with -g, only the app gets stripped);
|
|
||||||
# the bounds checks are compiled in, so a stripped exe still catches them.
|
|
||||||
find "$d/build_flatpak/tests" -type f -perm -u+x -exec strip --strip-unneeded {} + 2>/dev/null || true
|
|
||||||
# At runtime the tests read tests/ (TEST_DATA_DIR), scripts/, and under
|
|
||||||
# resources/ the shipped profiles (PROFILES_DIR) and the printers/ maps.
|
|
||||||
find "$d" -mindepth 1 -maxdepth 1 -type d \
|
|
||||||
! -name tests ! -name build_flatpak ! -name scripts ! -name resources -exec rm -rf {} +
|
|
||||||
find "$d/resources" -mindepth 1 -maxdepth 1 ! -name profiles ! -name printers -exec rm -rf {} +
|
|
||||||
tar -cf flatpak-test-asset.tar flatpak_app "$d"
|
|
||||||
- name: Upload flatpak test asset
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: ${{ github.sha }}-flatpak-tests-${{ matrix.variant.arch }}
|
|
||||||
path: flatpak-test-asset.tar
|
|
||||||
retention-days: 1
|
|
||||||
# keep-build-dirs would otherwise land in the flatpak-builder cache saved post-job.
|
|
||||||
- name: Drop the kept build dirs before the flatpak-builder cache saves
|
|
||||||
if: always()
|
|
||||||
shell: bash
|
|
||||||
run: rm -rf .flatpak-builder/build
|
|
||||||
unit_tests_flatpak_x86_64:
|
|
||||||
name: Flatpak x86_64
|
|
||||||
needs: flatpak
|
|
||||||
if: ${{ !cancelled() && success() }}
|
|
||||||
uses: ./.github/workflows/unit_tests_flatpak.yml
|
|
||||||
with:
|
|
||||||
os: ubuntu-24.04
|
|
||||||
artifact: ${{ github.sha }}-flatpak-tests-x86_64
|
|
||||||
unit_tests_flatpak_aarch64:
|
|
||||||
name: Flatpak aarch64
|
|
||||||
needs: flatpak
|
|
||||||
if: ${{ !cancelled() && success() }}
|
|
||||||
uses: ./.github/workflows/unit_tests_flatpak.yml
|
|
||||||
with:
|
|
||||||
os: ubuntu-24.04-arm
|
|
||||||
artifact: ${{ github.sha }}-flatpak-tests-aarch64
|
|
||||||
|
|||||||
@@ -9,10 +9,6 @@ on:
|
|||||||
arch:
|
arch:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
compiler:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: msvc
|
|
||||||
build-deps-only:
|
build-deps-only:
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -30,20 +26,16 @@ jobs:
|
|||||||
valid-cache: ${{ steps.cache_deps.outputs.cache-hit }}
|
valid-cache: ${{ steps.cache_deps.outputs.cache-hit }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
lfs: 'false'
|
lfs: 'false'
|
||||||
|
|
||||||
- name: set outputs
|
- name: set outputs
|
||||||
id: set_outputs
|
id: set_outputs
|
||||||
env:
|
env:
|
||||||
# Anything that changes how the tree is built belongs in the key, or a job
|
# Keep macOS cache keys and paths architecture-specific.
|
||||||
# restores one it cannot use. Linux amd64 passes no arch deliberately, so
|
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || 'linux-clang') }}
|
||||||
# 'linux-clang' keeps the cache it already has.
|
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || '/OrcaSlicer_dep' }}
|
||||||
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && format('windows-{0}-{1}', inputs.arch, inputs.compiler) || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
|
|
||||||
# The Windows ARM64 deps build in build-arm64, all others under build;
|
|
||||||
# build_deps.yml and build_orca.yml pass the Windows directory to build_win.bat.
|
|
||||||
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || (runner.os == 'Windows' && inputs.arch == 'arm64') && '-arm64/OrcaSlicer_dep' || '/OrcaSlicer_dep' }}
|
|
||||||
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
||||||
run: |
|
run: |
|
||||||
echo cache-key=${{ env.cache-os }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }}
|
echo cache-key=${{ env.cache-os }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }}
|
||||||
@@ -51,7 +43,7 @@ jobs:
|
|||||||
|
|
||||||
- name: load cache
|
- name: load cache
|
||||||
id: cache_deps
|
id: cache_deps
|
||||||
uses: actions/cache@v6
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ${{ steps.set_outputs.outputs.cache-path }}
|
path: ${{ steps.set_outputs.outputs.cache-path }}
|
||||||
key: ${{ steps.set_outputs.outputs.cache-key }}
|
key: ${{ steps.set_outputs.outputs.cache-key }}
|
||||||
@@ -67,7 +59,6 @@ jobs:
|
|||||||
valid-cache: ${{ needs.check_cache.outputs.valid-cache == 'true' }}
|
valid-cache: ${{ needs.check_cache.outputs.valid-cache == 'true' }}
|
||||||
os: ${{ inputs.os }}
|
os: ${{ inputs.os }}
|
||||||
arch: ${{ inputs.arch }}
|
arch: ${{ inputs.arch }}
|
||||||
compiler: ${{ inputs.compiler }}
|
|
||||||
build-deps-only: ${{ inputs.build-deps-only }}
|
build-deps-only: ${{ inputs.build-deps-only }}
|
||||||
force-build: ${{ inputs.force-build }}
|
force-build: ${{ inputs.force-build }}
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ on:
|
|||||||
arch:
|
arch:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
compiler:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: msvc
|
|
||||||
build-deps-only:
|
build-deps-only:
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -38,39 +34,22 @@ jobs:
|
|||||||
|
|
||||||
# Setup the environment
|
# Setup the environment
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
lfs: 'false'
|
lfs: 'false'
|
||||||
|
|
||||||
- name: load cached deps
|
- name: load cached deps
|
||||||
uses: actions/cache@v6
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ${{ inputs.cache-path }}
|
path: ${{ inputs.cache-path }}
|
||||||
key: ${{ inputs.cache-key }}
|
key: ${{ inputs.cache-key }}
|
||||||
|
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
# The windows-11-arm runner needs CMake <= 3.31 (handled in the next step).
|
|
||||||
if: ${{ !(runner.os == 'Windows' && inputs.arch == 'arm64') }}
|
|
||||||
with:
|
with:
|
||||||
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
||||||
useLocalCache: true # <--= Use the local cache (default is 'false').
|
useLocalCache: true # <--= Use the local cache (default is 'false').
|
||||||
useCloudCache: true
|
useCloudCache: true
|
||||||
|
|
||||||
- name: Install CMake 3.31.x (Windows ARM64)
|
|
||||||
# windows-11-arm ships CMake 4.x, which removed pre-3.5 policy
|
|
||||||
# compatibility AND has incomplete ASM_ARMASM linker modules
|
|
||||||
# (breaks Boost.Context on ARM64). Pin to the last 3.x release.
|
|
||||||
if: runner.os == 'Windows' && inputs.arch == 'arm64'
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$ver = "3.31.6"
|
|
||||||
$url = "https://github.com/Kitware/CMake/releases/download/v$ver/cmake-$ver-windows-arm64.zip"
|
|
||||||
Invoke-WebRequest -Uri $url -OutFile "$env:RUNNER_TEMP\cmake.zip"
|
|
||||||
Expand-Archive -Path "$env:RUNNER_TEMP\cmake.zip" -DestinationPath "$env:RUNNER_TEMP\cmake" -Force
|
|
||||||
$cmakeBin = "$env:RUNNER_TEMP\cmake\cmake-$ver-windows-arm64\bin"
|
|
||||||
if (-not (Test-Path "$cmakeBin\cmake.exe")) { throw "cmake.exe not found at $cmakeBin" }
|
|
||||||
Add-Content -Path $env:GITHUB_PATH -Value $cmakeBin
|
|
||||||
|
|
||||||
- name: setup dev on Windows
|
- name: setup dev on Windows
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: microsoft/setup-msbuild@v3
|
uses: microsoft/setup-msbuild@v3
|
||||||
@@ -86,50 +65,6 @@ jobs:
|
|||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
|
|
||||||
- name: Install MSYS2 (clangarm64) with GMP/MPFR and LLVM tools
|
|
||||||
if: runner.os == 'Windows' && inputs.arch == 'arm64'
|
|
||||||
uses: msys2/setup-msys2@v2
|
|
||||||
with:
|
|
||||||
msystem: CLANGARM64
|
|
||||||
update: true
|
|
||||||
install: >-
|
|
||||||
mingw-w64-clang-aarch64-gmp
|
|
||||||
mingw-w64-clang-aarch64-mpfr
|
|
||||||
mingw-w64-clang-aarch64-llvm
|
|
||||||
|
|
||||||
- name: Stage ARM64 GMP/MPFR (no prebuilt blobs exist for win-arm64)
|
|
||||||
# GMP/MPFR ship prebuilt x64/x86 blobs in-tree but none for ARM64.
|
|
||||||
# Pull them from MSYS2 clangarm64 and generate MSVC import libs via
|
|
||||||
# llvm-dlltool, then stage into deps/{GMP,MPFR}/.../win-arm64 where the
|
|
||||||
# MSVC branch of GMP.cmake/MPFR.cmake copies them into the dep prefix.
|
|
||||||
if: runner.os == 'Windows' && inputs.arch == 'arm64'
|
|
||||||
shell: msys2 {0}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
BIN=/clangarm64/bin
|
|
||||||
REPO=$(cygpath -u "$GITHUB_WORKSPACE")
|
|
||||||
|
|
||||||
make_import_lib() {
|
|
||||||
local dll="$1"; local lib="$2"; local def="/tmp/${dll%.dll}.def"
|
|
||||||
echo "EXPORTS" > "$def"
|
|
||||||
llvm-readobj --coff-exports "$BIN/$dll" | awk '/Name: /{print $2}' >> "$def"
|
|
||||||
llvm-dlltool -m arm64 -D "$dll" -d "$def" -l "$BIN/$lib"
|
|
||||||
}
|
|
||||||
|
|
||||||
make_import_lib libgmp-10.dll libgmp-10.lib
|
|
||||||
|
|
||||||
# MPFR 4.x ships as libmpfr-6.dll; rename to libmpfr-4 BEFORE generating
|
|
||||||
# the import lib so the baked-in runtime DLL name is correct.
|
|
||||||
MPFR_DLL=$(ls $BIN/libmpfr-*.dll | head -1 | xargs basename)
|
|
||||||
if [ "$MPFR_DLL" != "libmpfr-4.dll" ]; then cp "$BIN/$MPFR_DLL" "$BIN/libmpfr-4.dll"; fi
|
|
||||||
make_import_lib libmpfr-4.dll libmpfr-4.lib
|
|
||||||
|
|
||||||
mkdir -p $REPO/deps/GMP/gmp/lib/win-arm64 $REPO/deps/MPFR/mpfr/lib/win-arm64
|
|
||||||
cp $BIN/libgmp-10.dll $BIN/libgmp-10.lib $REPO/deps/GMP/gmp/lib/win-arm64/
|
|
||||||
cp $BIN/libmpfr-4.dll $BIN/libmpfr-4.lib $REPO/deps/MPFR/mpfr/lib/win-arm64/
|
|
||||||
cp /clangarm64/include/gmp.h $REPO/deps/GMP/gmp/include/
|
|
||||||
cp /clangarm64/include/mpfr.h $REPO/deps/MPFR/mpfr/include/ || true
|
|
||||||
|
|
||||||
# Build Dependencies
|
# Build Dependencies
|
||||||
- name: Build on Windows
|
- name: Build on Windows
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
@@ -138,11 +73,8 @@ jobs:
|
|||||||
if (-not "${{ vars.SELF_HOSTED }}") {
|
if (-not "${{ vars.SELF_HOSTED }}") {
|
||||||
choco install strawberryperl
|
choco install strawberryperl
|
||||||
}
|
}
|
||||||
# cache-path is the install directory inside the deps build directory.
|
.\build_release_vs.bat deps
|
||||||
$deps = (Split-Path "${{ inputs.cache-path }}").Replace('\', '/')
|
.\build_release_vs.bat pack
|
||||||
# -l compiles with Visual Studio's clang-cl and -x builds with Ninja; --msvc --msbuild is cl under the Visual Studio generator.
|
|
||||||
$flags = if ("${{ inputs.compiler }}" -eq "clang") { "-l", "-x" } else { "--msvc", "--msbuild" }
|
|
||||||
.\build_win.bat -d --arch ${{ inputs.arch }} --deps-dir $deps @flags
|
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
|
|
||||||
- name: Build on Mac ${{ inputs.arch }}
|
- name: Build on Mac ${{ inputs.arch }}
|
||||||
@@ -150,9 +82,9 @@ jobs:
|
|||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: |
|
run: |
|
||||||
if [ -z "${{ vars.SELF_HOSTED }}" ]; then
|
if [ -z "${{ vars.SELF_HOSTED }}" ]; then
|
||||||
brew install automake texinfo libtool pkgconf yasm nasm
|
brew install automake texinfo libtool
|
||||||
fi
|
fi
|
||||||
./build_release_macos.sh -dx ${{ !vars.SELF_HOSTED && '-j 3' || '' }} -a ${{ inputs.arch }} -t 10.15
|
./build_release_macos.sh -dx ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15
|
||||||
(cd "${{ github.workspace }}/deps/build/${{ inputs.arch }}" && \
|
(cd "${{ github.workspace }}/deps/build/${{ inputs.arch }}" && \
|
||||||
find . -mindepth 1 -maxdepth 1 ! -name 'OrcaSlicer_dep' -exec rm -rf {} +)
|
find . -mindepth 1 -maxdepth 1 ! -name 'OrcaSlicer_dep' -exec rm -rf {} +)
|
||||||
|
|
||||||
@@ -205,5 +137,4 @@ jobs:
|
|||||||
cache-path: ${{ inputs.cache-path }}
|
cache-path: ${{ inputs.cache-path }}
|
||||||
os: ${{ inputs.os }}
|
os: ${{ inputs.os }}
|
||||||
arch: ${{ inputs.arch }}
|
arch: ${{ inputs.arch }}
|
||||||
compiler: ${{ inputs.compiler }}
|
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
|||||||
@@ -13,10 +13,6 @@ on:
|
|||||||
arch:
|
arch:
|
||||||
required: false
|
required: false
|
||||||
type: string
|
type: string
|
||||||
compiler:
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: msvc
|
|
||||||
macos-combine-only:
|
macos-combine-only:
|
||||||
required: false
|
required: false
|
||||||
type: boolean
|
type: boolean
|
||||||
@@ -33,112 +29,27 @@ jobs:
|
|||||||
ubuntu-ver: '2404'
|
ubuntu-ver: '2404'
|
||||||
ubuntu-ver-str: '_Ubuntu2404'
|
ubuntu-ver-str: '_Ubuntu2404'
|
||||||
ORCA_UPDATER_SIG_KEY: ${{ secrets.ORCA_UPDATER_SIG_KEY }}
|
ORCA_UPDATER_SIG_KEY: ${{ secrets.ORCA_UPDATER_SIG_KEY }}
|
||||||
# Branches whose builds are published to the nightly release. The
|
|
||||||
# belt-printer branch ships alongside main but its assets carry a `_belt`
|
|
||||||
# suffix (nightly_suffix) so they never overwrite the main nightly assets.
|
|
||||||
deploy_nightly: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/belt-printer' }}
|
|
||||||
nightly_suffix: ${{ github.ref == 'refs/heads/belt-printer' && '_belt' || '' }}
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
lfs: 'false'
|
lfs: 'false'
|
||||||
|
|
||||||
- name: load cached deps
|
- name: load cached deps
|
||||||
if: ${{ !(runner.os == 'macOS' && inputs.macos-combine-only) }}
|
if: ${{ !(runner.os == 'macOS' && inputs.macos-combine-only) }}
|
||||||
uses: actions/cache@v6
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ${{ inputs.cache-path }}
|
path: ${{ inputs.cache-path }}
|
||||||
key: ${{ inputs.cache-key }}
|
key: ${{ inputs.cache-key }}
|
||||||
fail-on-cache-miss: true
|
fail-on-cache-miss: true
|
||||||
|
|
||||||
- uses: lukka/get-cmake@latest
|
- uses: lukka/get-cmake@latest
|
||||||
# The windows-11-arm runner needs CMake <= 3.31 (handled in the next step).
|
|
||||||
if: ${{ !(runner.os == 'Windows' && inputs.arch == 'arm64') }}
|
|
||||||
with:
|
with:
|
||||||
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
||||||
useLocalCache: true # <--= Use the local cache (default is 'false').
|
useLocalCache: true # <--= Use the local cache (default is 'false').
|
||||||
useCloudCache: true
|
useCloudCache: true
|
||||||
|
|
||||||
- name: Install CMake 3.31.x (Windows ARM64)
|
|
||||||
# windows-11-arm ships CMake 4.x, which removed pre-3.5 policy
|
|
||||||
# compatibility AND has incomplete ASM_ARMASM linker modules
|
|
||||||
# (breaks Boost.Context on ARM64). Pin to the last 3.x release.
|
|
||||||
if: runner.os == 'Windows' && inputs.arch == 'arm64'
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
$ver = "3.31.6"
|
|
||||||
$url = "https://github.com/Kitware/CMake/releases/download/v$ver/cmake-$ver-windows-arm64.zip"
|
|
||||||
Invoke-WebRequest -Uri $url -OutFile "$env:RUNNER_TEMP\cmake.zip"
|
|
||||||
Expand-Archive -Path "$env:RUNNER_TEMP\cmake.zip" -DestinationPath "$env:RUNNER_TEMP\cmake" -Force
|
|
||||||
$cmakeBin = "$env:RUNNER_TEMP\cmake\cmake-$ver-windows-arm64\bin"
|
|
||||||
if (-not (Test-Path "$cmakeBin\cmake.exe")) { throw "cmake.exe not found at $cmakeBin" }
|
|
||||||
Add-Content -Path $env:GITHUB_PATH -Value $cmakeBin
|
|
||||||
|
|
||||||
# Compiler cache. Pushes save it, so main keeps it warm; pull requests
|
|
||||||
# restore it and discard what they compiled. Objects are keyed on the
|
|
||||||
# preprocessed source, the compiler and the flags, so a leg only ever
|
|
||||||
# hits its own entries. A failed install costs the caching, not the build.
|
|
||||||
- name: Name the compiler cache leg
|
|
||||||
if: ${{ !inputs.macos-combine-only }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
leg="${{ runner.os }}-${{ inputs.arch || 'amd64' }}${{ runner.os == 'Windows' && format('-{0}', inputs.compiler) || '' }}"
|
|
||||||
# clang-cl refuses a precompiled header from another cl.exe build and ccache
|
|
||||||
# does not hash that build, so each one gets its own cache. The build number
|
|
||||||
# is read from cl.exe itself; the toolset directory keeps its name across patches.
|
|
||||||
if [ "${{ runner.os }}" = Windows ]; then
|
|
||||||
vswhere='/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe'
|
|
||||||
toolset=$(tr -d '\r\n' < "$("$vswhere" -latest -products '*' -find 'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt' | tr -d '\r')")
|
|
||||||
cl=$("$vswhere" -latest -products '*' -find 'VC\Tools\MSVC\'"$toolset"'\**\cl.exe' | tr -d '\r' | head -1)
|
|
||||||
leg="$leg-vc$("$cl" 2>&1 | grep -o -E 'Version [0-9.]+' | cut -d' ' -f2)"
|
|
||||||
fi
|
|
||||||
echo "CCACHE_LEG=$leg" >> "$GITHUB_ENV"
|
|
||||||
echo "CCACHE_ENTRY=ccache-$leg-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
# The action only installs and configures ccache. Restore and save go
|
|
||||||
# through actions/cache with one path string, since the cache service
|
|
||||||
# only matches entries saved under the identical path and the action
|
|
||||||
# spells it differently on Windows.
|
|
||||||
- name: Compiler cache
|
|
||||||
id: ccache
|
|
||||||
if: ${{ !inputs.macos-combine-only }}
|
|
||||||
continue-on-error: true
|
|
||||||
uses: hendrikmuhs/ccache-action@v1.2.24
|
|
||||||
with:
|
|
||||||
key: ${{ env.CCACHE_LEG }}
|
|
||||||
max-size: 3G
|
|
||||||
restore: false
|
|
||||||
save: false
|
|
||||||
# ccache -s runs as its own step; no summary table per job.
|
|
||||||
job-summary: ''
|
|
||||||
|
|
||||||
- name: Restore compiler cache
|
|
||||||
id: ccache_restore
|
|
||||||
if: ${{ steps.ccache.outcome == 'success' }}
|
|
||||||
uses: actions/cache/restore@v6
|
|
||||||
with:
|
|
||||||
path: ${{ github.workspace }}/.ccache
|
|
||||||
key: ${{ env.CCACHE_ENTRY }}
|
|
||||||
restore-keys: ccache-${{ env.CCACHE_LEG }}-
|
|
||||||
|
|
||||||
- name: Enable compiler cache
|
|
||||||
if: ${{ steps.ccache.outcome == 'success' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "CMAKE_C_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV"
|
|
||||||
echo "CMAKE_CXX_COMPILER_LAUNCHER=ccache" >> "$GITHUB_ENV"
|
|
||||||
# Headers a fresh checkout has just written, the few files that
|
|
||||||
# use __DATE__ or __TIME__, and the precompiled header, whose
|
|
||||||
# macros ccache cannot see.
|
|
||||||
echo "CCACHE_SLOPPINESS=pch_defines,time_macros,include_file_mtime,include_file_ctime" >> "$GITHUB_ENV"
|
|
||||||
# Hash the includes the compiler reports instead of preprocessing
|
|
||||||
# every miss before compiling it.
|
|
||||||
echo "CCACHE_DEPEND=1" >> "$GITHUB_ENV"
|
|
||||||
# The restored directory carries the previous run's counters.
|
|
||||||
ccache -z
|
|
||||||
|
|
||||||
- name: Get the version and date on Ubuntu and macOS
|
- name: Get the version and date on Ubuntu and macOS
|
||||||
if: runner.os != 'Windows'
|
if: runner.os != 'Windows'
|
||||||
run: |
|
run: |
|
||||||
@@ -154,11 +65,6 @@ jobs:
|
|||||||
echo "ver_pure=$ver_pure" >> $GITHUB_ENV
|
echo "ver_pure=$ver_pure" >> $GITHUB_ENV
|
||||||
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||||
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
|
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
|
||||||
# Per-arch Linux AppImage naming: amd64 keeps the historical unsuffixed
|
|
||||||
# name (arch_suffix empty). Unused on macOS/Windows.
|
|
||||||
if [ '${{ inputs.arch }}' = 'aarch64' ]; then
|
|
||||||
echo "arch_suffix=_aarch64" >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Get the version and date on Windows
|
- name: Get the version and date on Windows
|
||||||
@@ -208,34 +114,8 @@ jobs:
|
|||||||
- name: Build slicer mac
|
- name: Build slicer mac
|
||||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
# arm64 only: build the tests here; the unit_tests_macos job runs them.
|
|
||||||
env:
|
|
||||||
ORCA_TESTS_BUILD_ONLY: ${{ inputs.arch == 'arm64' && '1' || '' }}
|
|
||||||
run: |
|
run: |
|
||||||
./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-j 3' || '' }} -a ${{ inputs.arch }} -t 10.15 ${{ inputs.arch == 'arm64' && '-T' || '' }}
|
./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15
|
||||||
|
|
||||||
- name: Pack unit tests mac
|
|
||||||
if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64'
|
|
||||||
working-directory: ${{ github.workspace }}
|
|
||||||
run: tar -cvf build_tests.tar build/arm64/tests
|
|
||||||
|
|
||||||
- name: Upload Test Artifact mac
|
|
||||||
if: runner.os == 'macOS' && !inputs.macos-combine-only && inputs.arch == 'arm64'
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: ${{ github.sha }}-tests-macos-arm64
|
|
||||||
overwrite: true
|
|
||||||
path: build_tests.tar
|
|
||||||
retention-days: 5
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Build system preset cache (macOS)
|
|
||||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
|
||||||
working-directory: ${{ github.workspace }}
|
|
||||||
shell: bash
|
|
||||||
# The bundle was already packed from resources/, so the caches have to be
|
|
||||||
# installed into it here; the source tree keeps its JSONs for later jobs.
|
|
||||||
run: ./scripts/build_preset_cache.sh -b build/${{ inputs.arch }} build/${{ inputs.arch }}/OrcaSlicer/OrcaSlicer.app/Contents/Resources/profiles
|
|
||||||
|
|
||||||
- name: Pack macOS app bundle ${{ inputs.arch }}
|
- name: Pack macOS app bundle ${{ inputs.arch }}
|
||||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
||||||
@@ -271,11 +151,19 @@ jobs:
|
|||||||
if: runner.os == 'macOS' && inputs.macos-combine-only
|
if: runner.os == 'macOS' && inputs.macos-combine-only
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: |
|
run: |
|
||||||
./build_release_macos.sh -u -x ${{ !vars.SELF_HOSTED && '-j 3' || '' }} -a universal -t 10.15
|
./build_release_macos.sh -u -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a universal -t 10.15
|
||||||
|
|
||||||
|
- name: Delete intermediate per-arch artifacts
|
||||||
|
if: runner.os == 'macOS' && inputs.macos-combine-only
|
||||||
|
uses: geekyeggo/delete-artifact@v6
|
||||||
|
with:
|
||||||
|
name: |
|
||||||
|
OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
|
||||||
|
OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
|
||||||
|
|
||||||
# Thanks to RaySajuuk, it's working now
|
# Thanks to RaySajuuk, it's working now
|
||||||
- name: Sign app and notary
|
- name: Sign app and notary
|
||||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/belt-printer' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'macOS' && inputs.macos-combine-only
|
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'macOS' && inputs.macos-combine-only
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
env:
|
env:
|
||||||
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
||||||
@@ -283,8 +171,6 @@ jobs:
|
|||||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||||
CERTIFICATE_ID: ${{ secrets.MACOS_CERTIFICATE_ID }}
|
CERTIFICATE_ID: ${{ secrets.MACOS_CERTIFICATE_ID }}
|
||||||
run: |
|
run: |
|
||||||
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
|
|
||||||
source ${{ github.workspace }}/scripts/retry.sh
|
|
||||||
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
||||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||||
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH
|
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH
|
||||||
@@ -296,41 +182,10 @@ jobs:
|
|||||||
security import $CERTIFICATE_PATH -P $P12_PASSWORD -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
security import $CERTIFICATE_PATH -P $P12_PASSWORD -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
||||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $P12_PASSWORD $KEYCHAIN_PATH
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $P12_PASSWORD $KEYCHAIN_PATH
|
||||||
# codesign --deep is deprecated and cannot sign the bundled Python
|
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app
|
||||||
# runtime (see relocate_python_runtime in build_release_macos.sh).
|
|
||||||
# Sign every nested Mach-O in the bundle explicitly -- notarization
|
|
||||||
# requires the hardened-runtime signature on each -- then seal the
|
|
||||||
# bundle itself last, and verify so coverage gaps fail at CI time
|
|
||||||
# rather than at notarization.
|
|
||||||
ENTITLEMENTS=${{ github.workspace }}/scripts/disable_validation.entitlements
|
|
||||||
sign() {
|
|
||||||
codesign --force --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" "$@"
|
|
||||||
}
|
|
||||||
sign_app() {
|
|
||||||
local app="$1"
|
|
||||||
local machos=()
|
|
||||||
# file is batched via xargs (per-file it costs minutes); universal
|
|
||||||
# binaries also print per-architecture lines, dropped by grep -v.
|
|
||||||
while IFS= read -r f; do machos+=("$f"); done < <(
|
|
||||||
find "$app" -type f -print0 |
|
|
||||||
xargs -0 file --no-pad --mime-type -- |
|
|
||||||
grep -v ' (for architecture ' |
|
|
||||||
grep ': application/x-mach-binary$' |
|
|
||||||
sed 's|: application/x-mach-binary$||'
|
|
||||||
)
|
|
||||||
if [ "${#machos[@]}" -eq 0 ]; then
|
|
||||||
echo "ERROR: no Mach-O files found in $app -- detection is broken" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "signing ${#machos[@]} Mach-O files in $app"
|
|
||||||
sign "${machos[@]}"
|
|
||||||
sign --verbose "$app"
|
|
||||||
codesign --verify --deep --strict --verbose=2 "$app"
|
|
||||||
}
|
|
||||||
sign_app "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app"
|
|
||||||
# Sign OrcaSlicer_profile_validator.app if it exists
|
# Sign OrcaSlicer_profile_validator.app if it exists
|
||||||
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
||||||
sign_app "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app"
|
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create main OrcaSlicer DMG without the profile validator helper
|
# Create main OrcaSlicer DMG without the profile validator helper
|
||||||
@@ -338,8 +193,8 @@ jobs:
|
|||||||
rm -rf ${{ 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/
|
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
|
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
|
||||||
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||||
|
|
||||||
# Create separate OrcaSlicer_profile_validator DMG if the app exists
|
# Create separate OrcaSlicer_profile_validator DMG if the app exists
|
||||||
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
||||||
@@ -347,8 +202,8 @@ jobs:
|
|||||||
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
|
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
|
||||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
|
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
|
||||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
|
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
|
||||||
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements "$ENTITLEMENTS" --sign "$CERTIFICATE_ID" OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Notarize main DMG
|
# Notarize main DMG
|
||||||
@@ -362,16 +217,14 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Create DMG without notary
|
- name: Create DMG without notary
|
||||||
if: github.ref != 'refs/heads/main' && github.ref != 'refs/heads/belt-printer' && runner.os == 'macOS' && inputs.macos-combine-only
|
if: github.ref != 'refs/heads/main' && runner.os == 'macOS' && inputs.macos-combine-only
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: |
|
run: |
|
||||||
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
|
|
||||||
source ${{ github.workspace }}/scripts/retry.sh
|
|
||||||
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
|
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
|
||||||
rm -rf ${{ 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/
|
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
|
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
|
||||||
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||||
|
|
||||||
# Create separate OrcaSlicer_profile_validator DMG if the app exists
|
# Create separate OrcaSlicer_profile_validator DMG if the app exists
|
||||||
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
||||||
@@ -379,19 +232,9 @@ jobs:
|
|||||||
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
|
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
|
||||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
|
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
|
||||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
|
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
|
||||||
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Delete the per-arch bundles only after signing/DMG creation succeeded, so a
|
|
||||||
# failed run keeps them available for a re-run instead of forcing a full rebuild.
|
|
||||||
- name: Delete intermediate per-arch artifacts
|
|
||||||
if: success() && runner.os == 'macOS' && inputs.macos-combine-only
|
|
||||||
uses: geekyeggo/delete-artifact@v6
|
|
||||||
with:
|
|
||||||
name: |
|
|
||||||
OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
|
|
||||||
OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
|
|
||||||
|
|
||||||
- name: Upload artifacts mac
|
- name: Upload artifacts mac
|
||||||
if: runner.os == 'macOS' && inputs.macos-combine-only
|
if: runner.os == 'macOS' && inputs.macos-combine-only
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
@@ -408,13 +251,13 @@ jobs:
|
|||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
- name: Deploy Mac release
|
- name: Deploy Mac release
|
||||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && env.deploy_nightly == 'true' && runner.os == 'macOS' && inputs.macos-combine-only && !vars.SELF_HOSTED
|
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'macOS' && inputs.macos-combine-only && !vars.SELF_HOSTED
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
release_id: 137995723
|
release_id: 137995723
|
||||||
asset_path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
asset_path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||||
asset_name: OrcaSlicer_Mac_universal_nightly${{ env.nightly_suffix }}.dmg
|
asset_name: OrcaSlicer_Mac_universal_nightly.dmg
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
||||||
|
|
||||||
@@ -430,18 +273,6 @@ jobs:
|
|||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
- name: Set Windows build variables
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
if ("${{ inputs.arch }}" -eq "arm64") {
|
|
||||||
"BUILD_DIR=build-arm64" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
|
|
||||||
"ARCH_SUFFIX=_arm64" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
|
|
||||||
} else {
|
|
||||||
"BUILD_DIR=build" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
|
|
||||||
"ARCH_SUFFIX=_x64" | Out-File -Append -FilePath $env:GITHUB_ENV -Encoding utf8
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: setup MSVC
|
- name: setup MSVC
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: microsoft/setup-msbuild@v3
|
uses: microsoft/setup-msbuild@v3
|
||||||
@@ -459,55 +290,23 @@ jobs:
|
|||||||
# env:
|
# env:
|
||||||
# WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
|
# WindowsSdkDir: 'C:\Program Files (x86)\Windows Kits\10\'
|
||||||
# WindowsSDKVersion: '10.0.26100.0\'
|
# WindowsSDKVersion: '10.0.26100.0\'
|
||||||
# --tests builds the unit tests too; the unit_tests_windows_* jobs run them.
|
run: .\build_release_vs.bat slicer
|
||||||
run: |
|
|
||||||
# cache-path is the install directory inside the deps build directory.
|
|
||||||
$deps = (Split-Path "${{ inputs.cache-path }}").Replace('\', '/')
|
|
||||||
# -l compiles with Visual Studio's clang-cl and -x builds with Ninja; --msvc --msbuild is cl under the Visual Studio generator.
|
|
||||||
$flags = if ("${{ inputs.compiler }}" -eq "clang") { "-l", "-x" } else { "--msvc", "--msbuild" }
|
|
||||||
.\build_win.bat -s --tests -i --arch ${{ inputs.arch }} --build-dir $env:BUILD_DIR --deps-dir $deps @flags
|
|
||||||
shell: pwsh
|
|
||||||
|
|
||||||
- name: Build system preset cache (Windows)
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
shell: cmd
|
|
||||||
# Shipped into both the already-installed tree (portable zip, MSIX) and
|
|
||||||
# the checkout cpack re-installs from when it builds the NSIS installer.
|
|
||||||
run: scripts\build_preset_cache.bat --prune-source "%BUILD_DIR%" "resources\profiles" "%BUILD_DIR%\OrcaSlicer\resources\profiles"
|
|
||||||
|
|
||||||
- name: Pack unit tests Win
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
working-directory: ${{ github.workspace }}
|
|
||||||
shell: pwsh
|
|
||||||
run: tar -cvf build_tests.tar ${{ env.BUILD_DIR }}/tests
|
|
||||||
|
|
||||||
- name: Upload Test Artifact Win
|
|
||||||
if: runner.os == 'Windows'
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: ${{ github.sha }}-tests-windows-${{ inputs.arch }}
|
|
||||||
overwrite: true
|
|
||||||
path: build_tests.tar
|
|
||||||
retention-days: 5
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
# NSIS is x86-only; it runs (and the installer it emits runs) under ARM64's
|
|
||||||
# x86 emulation, packaging the native arm64 payload from build-arm64.
|
|
||||||
- name: Create installer Win
|
- name: Create installer Win
|
||||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
working-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}
|
working-directory: ${{ github.workspace }}/build
|
||||||
run: |
|
run: |
|
||||||
cpack -G NSIS
|
cpack -G NSIS
|
||||||
|
|
||||||
- name: Pack app
|
- name: Pack app
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
working-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}
|
working-directory: ${{ github.workspace }}/build
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: '"C:/Program Files/7-Zip/7z.exe" a -tzip OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable.zip ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer'
|
run: '"C:/Program Files/7-Zip/7z.exe" a -tzip OrcaSlicer_Windows_${{ env.ver }}_portable.zip ${{ github.workspace }}/build/OrcaSlicer'
|
||||||
|
|
||||||
- name: Pack PDB
|
- name: Pack PDB
|
||||||
if: runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
|
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
working-directory: ${{ github.workspace }}/${{ env.BUILD_DIR }}/src/Release
|
working-directory: ${{ github.workspace }}/build/src/Release
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: '"C:/Program Files/7-Zip/7z.exe" a -m0=lzma2 -mx9 Debug_PDB_${{ env.ver }}_for_developers_only.7z *.pdb'
|
run: '"C:/Program Files/7-Zip/7z.exe" a -m0=lzma2 -mx9 Debug_PDB_${{ env.ver }}_for_developers_only.7z *.pdb'
|
||||||
|
|
||||||
@@ -515,54 +314,54 @@ jobs:
|
|||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable
|
name: OrcaSlicer_Windows_${{ env.ver }}_portable
|
||||||
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer
|
path: ${{ github.workspace }}/build/OrcaSlicer
|
||||||
|
|
||||||
- name: Upload artifacts Win installer
|
- name: Upload artifacts Win installer
|
||||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}
|
name: OrcaSlicer_Windows_${{ env.ver }}
|
||||||
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer*.exe
|
path: ${{ github.workspace }}/build/OrcaSlicer*.exe
|
||||||
|
|
||||||
- name: Upload artifacts Win PDB
|
- name: Upload artifacts Win PDB
|
||||||
if: runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
|
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: PDB
|
name: PDB
|
||||||
path: ${{ github.workspace }}/build/src/Release/Debug_PDB_${{ env.ver }}_for_developers_only.7z
|
path: ${{ github.workspace }}/build/src/Release/Debug_PDB_${{ env.ver }}_for_developers_only.7z
|
||||||
|
|
||||||
- name: Upload OrcaSlicer_profile_validator Win
|
- name: Upload OrcaSlicer_profile_validator Win
|
||||||
if: runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
|
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer_profile_validator_Windows_${{ env.ver }}
|
name: OrcaSlicer_profile_validator_Windows_${{ env.ver }}
|
||||||
path: ${{ github.workspace }}/build/src/Release/OrcaSlicer_profile_validator.exe
|
path: ${{ github.workspace }}/build/src/Release/OrcaSlicer_profile_validator.exe
|
||||||
|
|
||||||
- name: Deploy Windows release portable
|
- name: Deploy Windows release portable
|
||||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && env.deploy_nightly == 'true' && runner.os == 'Windows' && !vars.SELF_HOSTED
|
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
release_id: 137995723
|
release_id: 137995723
|
||||||
asset_path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_${{ env.ver }}${{ env.ARCH_SUFFIX }}_portable.zip
|
asset_path: ${{ github.workspace }}/build/OrcaSlicer_Windows_${{ env.ver }}_portable.zip
|
||||||
asset_name: OrcaSlicer_Windows${{ env.ARCH_SUFFIX }}_nightly${{ env.nightly_suffix }}_portable.zip
|
asset_name: OrcaSlicer_Windows_nightly_portable.zip
|
||||||
asset_content_type: application/x-zip-compressed
|
asset_content_type: application/x-zip-compressed
|
||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
- name: Deploy Windows release installer
|
- name: Deploy Windows release installer
|
||||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && env.deploy_nightly == 'true' && runner.os == 'Windows' && !vars.SELF_HOSTED
|
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
release_id: 137995723
|
release_id: 137995723
|
||||||
asset_path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_Installer_${{ env.ver }}${{ env.ARCH_SUFFIX }}.exe
|
asset_path: ${{ github.workspace }}/build/OrcaSlicer_Windows_Installer_${{ env.ver }}.exe
|
||||||
asset_name: OrcaSlicer_Windows_Installer${{ env.ARCH_SUFFIX }}_nightly${{ env.nightly_suffix }}.exe
|
asset_name: OrcaSlicer_Windows_Installer_nightly.exe
|
||||||
asset_content_type: application/x-msdownload
|
asset_content_type: application/x-msdownload
|
||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
- name: Deploy Windows OrcaSlicer_profile_validator release
|
- name: Deploy Windows OrcaSlicer_profile_validator release
|
||||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Windows' && inputs.arch != 'arm64' && !vars.SELF_HOSTED
|
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
@@ -572,26 +371,6 @@ jobs:
|
|||||||
asset_content_type: application/x-msdownload
|
asset_content_type: application/x-msdownload
|
||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
- name: Build MSIX Store package Win
|
|
||||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
|
||||||
working-directory: ${{ github.workspace }}
|
|
||||||
shell: pwsh
|
|
||||||
run: |
|
|
||||||
./scripts/msix/build_msix.ps1 `
|
|
||||||
-InstallDir "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer" `
|
|
||||||
-OutputPath "${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_MSIX_${{ env.ver }}${{ env.ARCH_SUFFIX }}.msix" `
|
|
||||||
-Architecture "${{ inputs.arch }}" `
|
|
||||||
-IdentityName "${{ vars.ORCA_MSIX_IDENTITY_NAME || 'OrcaSlicer.OrcaSlicer' }}" `
|
|
||||||
-Publisher "${{ vars.ORCA_MSIX_PUBLISHER || 'CN=38F7EA55-C73B-4072-B3B2-C8E0EA15BB82' }}" `
|
|
||||||
-PublisherDisplayName "${{ vars.ORCA_MSIX_PUBLISHER_DISPLAY_NAME || 'OrcaSlicer' }}"
|
|
||||||
|
|
||||||
- name: Upload artifacts Win MSIX
|
|
||||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: OrcaSlicer_Windows_MSIX_${{ env.ver }}${{ env.ARCH_SUFFIX }}
|
|
||||||
path: ${{ github.workspace }}/${{ env.BUILD_DIR }}/OrcaSlicer_Windows_MSIX_${{ env.ver }}${{ env.ARCH_SUFFIX }}.msix
|
|
||||||
|
|
||||||
# Ubuntu
|
# Ubuntu
|
||||||
- name: Apt-Install Dependencies
|
- name: Apt-Install Dependencies
|
||||||
if: runner.os == 'Linux' && !vars.SELF_HOSTED
|
if: runner.os == 'Linux' && !vars.SELF_HOSTED
|
||||||
@@ -603,14 +382,11 @@ jobs:
|
|||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Build + tar the unit tests (-t) on both Linux legs so each arch
|
|
||||||
# (x86_64 + aarch64) gets tested by its own unit_tests_linux_* job.
|
|
||||||
./build_linux.sh -istrlL
|
./build_linux.sh -istrlL
|
||||||
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
|
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
|
||||||
appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||||
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage"
|
chmod +x ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||||
chmod +x "$appimage"
|
tar -cvpf build_tests.tar build/tests
|
||||||
tar -cvf build_tests.tar build/tests
|
|
||||||
|
|
||||||
# Use tar because upload-artifacts won't always preserve directory structure
|
# Use tar because upload-artifacts won't always preserve directory structure
|
||||||
# and doesn't preserve file permissions
|
# and doesn't preserve file permissions
|
||||||
@@ -618,42 +394,14 @@ jobs:
|
|||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: ${{ github.sha }}-tests-linux-${{ inputs.arch == 'aarch64' && 'aarch64' || 'x86_64' }}
|
name: ${{ github.sha }}-tests
|
||||||
overwrite: true
|
overwrite: true
|
||||||
path: build_tests.tar
|
path: build_tests.tar
|
||||||
retention-days: 5
|
retention-days: 5
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Build system preset cache (Linux)
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# Both were packed from resources/ before the caches existed, so the
|
|
||||||
# AppImage is unpacked first and the caches shipped into it and into
|
|
||||||
# the package tree; the source tree keeps its JSONs for later steps.
|
|
||||||
appimage=$(find build -maxdepth 1 -name "OrcaSlicer_Linux_AppImage*.AppImage" | head -1)
|
|
||||||
chmod +x "$appimage"
|
|
||||||
"$appimage" --appimage-extract
|
|
||||||
./scripts/build_preset_cache.sh -b build build/package/resources/profiles squashfs-root/resources/profiles
|
|
||||||
appimagetool=$(find build -name "appimagetool.AppImage" | head -1)
|
|
||||||
ARCH=$(uname -m) "$appimagetool" --appimage-extract-and-run squashfs-root "$appimage"
|
|
||||||
rm -rf squashfs-root
|
|
||||||
# Ship the freshly-built validator so slice_check_linux (build_all.yml)
|
|
||||||
# can slice-sweep the shipped profiles with this PR's engine. Taken from
|
|
||||||
# the aarch64 leg so the sweep also exercises the arm build; x86_64 on
|
|
||||||
# SELF_HOSTED, which skips arm64.
|
|
||||||
- name: Upload profile validator (for slice check)
|
|
||||||
if: ${{ runner.os == 'Linux' && (vars.SELF_HOSTED && inputs.arch != 'aarch64' || !vars.SELF_HOSTED && inputs.arch == 'aarch64') }}
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: ${{ github.sha }}-profile-validator-linux-${{ inputs.arch == 'aarch64' && 'aarch64' || 'x86_64' }}
|
|
||||||
overwrite: true
|
|
||||||
path: ./build/src/Release/OrcaSlicer_profile_validator
|
|
||||||
retention-days: 5
|
|
||||||
if-no-files-found: error
|
|
||||||
|
|
||||||
- name: Run external slicer regression tests
|
- name: Run external slicer regression tests
|
||||||
if: runner.os == 'Linux' && inputs.arch != 'aarch64'
|
if: runner.os == 'Linux'
|
||||||
timeout-minutes: 20
|
timeout-minutes: 20
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -663,7 +411,7 @@ jobs:
|
|||||||
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
|
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
|
||||||
|
|
||||||
- name: Build orca_custom_preset_tests
|
- name: Build orca_custom_preset_tests
|
||||||
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64'
|
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED
|
||||||
working-directory: ${{ github.workspace }}/build/src/Release
|
working-directory: ${{ github.workspace }}/build/src/Release
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@@ -675,28 +423,28 @@ jobs:
|
|||||||
if: ${{ ! env.ACT && runner.os == 'Linux' }}
|
if: ${{ ! env.ACT && runner.os == 'Linux' }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}${{ env.arch_suffix }}_${{ env.ver }}
|
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||||
path: "./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage"
|
path: './build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage'
|
||||||
|
|
||||||
- name: Upload OrcaSlicer_profile_validator Ubuntu
|
- name: Upload OrcaSlicer_profile_validator Ubuntu
|
||||||
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||||
path: './build/src/Release/OrcaSlicer_profile_validator'
|
path: './build/src/Release/OrcaSlicer_profile_validator'
|
||||||
|
|
||||||
- name: Deploy Ubuntu release
|
- name: Deploy Ubuntu release
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && env.deploy_nightly == 'true' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
release_id: 137995723
|
release_id: 137995723
|
||||||
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||||
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_nightly${{ env.nightly_suffix }}.AppImage
|
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_nightly.AppImage
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
||||||
- name: Deploy Ubuntu release
|
- name: Deploy Ubuntu release
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||||
uses: rickstaa/action-create-tag@v1
|
uses: rickstaa/action-create-tag@v1
|
||||||
with:
|
with:
|
||||||
tag: "nightly-builds"
|
tag: "nightly-builds"
|
||||||
@@ -705,7 +453,7 @@ jobs:
|
|||||||
message: "nightly-builds"
|
message: "nightly-builds"
|
||||||
|
|
||||||
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
|
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
@@ -716,7 +464,7 @@ jobs:
|
|||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
- name: Deploy orca_custom_preset_tests
|
- name: Deploy orca_custom_preset_tests
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
with:
|
with:
|
||||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||||
@@ -725,41 +473,3 @@ jobs:
|
|||||||
asset_name: orca_custom_preset_tests.zip
|
asset_name: orca_custom_preset_tests.zip
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
# The build has just touched everything it can use, so an object
|
|
||||||
# untouched for a week is dead, usually orphaned by a flag change.
|
|
||||||
- name: Compiler cache statistics
|
|
||||||
if: ${{ always() && steps.ccache.outcome == 'success' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
ccache --evict-older-than 7d
|
|
||||||
ccache -s -v || ccache -s
|
|
||||||
|
|
||||||
# Entries are immutable, so the new one is saved first and the older
|
|
||||||
# ones for this leg on this ref are dropped afterwards: a failed save
|
|
||||||
# leaves the previous entry in place. A cancelled or failed build saves
|
|
||||||
# too, since what it compiled is still valid; a restore that did not
|
|
||||||
# finish does not, since the directory may be a truncated copy.
|
|
||||||
- name: Save compiler cache
|
|
||||||
id: ccache_save
|
|
||||||
if: ${{ always() && steps.ccache_restore.outcome == 'success' && github.event_name != 'pull_request' }}
|
|
||||||
uses: actions/cache/save@v6
|
|
||||||
with:
|
|
||||||
path: ${{ github.workspace }}/.ccache
|
|
||||||
key: ${{ env.CCACHE_ENTRY }}
|
|
||||||
|
|
||||||
- name: Drop older compiler cache entries
|
|
||||||
if: ${{ always() && steps.ccache_save.outcome == 'success' }}
|
|
||||||
# A read-only token (fork PRs) cannot delete; that only costs storage.
|
|
||||||
# Older means a lower run id, so two runs finishing close together keep
|
|
||||||
# the newer entry whichever of them cleans up last.
|
|
||||||
continue-on-error: true
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
run: |
|
|
||||||
gh cache list --ref "$GITHUB_REF" --key "ccache-$CCACHE_LEG-" --limit 100 --json id,key \
|
|
||||||
| jq -r --arg prefix "ccache-$CCACHE_LEG-" --argjson run "$GITHUB_RUN_ID" \
|
|
||||||
'.[] | select((.key | ltrimstr($prefix) | split("-")[0] | tonumber?) < $run) | .id' \
|
|
||||||
| tr -d '\r' \
|
|
||||||
| while read -r id; do gh cache delete "$id"; done
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install gettext
|
- name: Install gettext
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,19 +1,10 @@
|
|||||||
name: Check profiles
|
name: Check profiles
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
# release/* is included because pr-merge-bot.yml lets delegates merge into
|
|
||||||
# it, and it gates on this workflow's result. Without it a delegated merge
|
|
||||||
# into a release branch would run no profile validation at all.
|
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- release/*
|
|
||||||
paths:
|
paths:
|
||||||
- 'resources/profiles/**'
|
- 'resources/profiles/**'
|
||||||
# orca_profile_tool.py also validates resources/printers/bambu_filament_ids.json, and
|
|
||||||
# both it and its tests live in scripts/, so a PR touching only those must still run
|
|
||||||
# this workflow.
|
|
||||||
- 'resources/printers/**'
|
|
||||||
- 'scripts/**'
|
|
||||||
- ".github/workflows/check_profiles.yml"
|
- ".github/workflows/check_profiles.yml"
|
||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -29,41 +20,28 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check_profiles:
|
check_profiles:
|
||||||
# This job name is the check-run name pr-merge-bot.yml requires before a
|
|
||||||
# delegated merge. Renaming it silently disables that gate.
|
|
||||||
name: Check profiles
|
name: Check profiles
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-24.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
# Deliberately not continue-on-error, unlike every check below: if the tool itself is
|
- name: Run extra JSON check
|
||||||
# broken, nothing it then reports about the profiles is worth reading.
|
id: extra_json_check
|
||||||
- name: Run the profile tool's own unit tests
|
|
||||||
run: python3 -m unittest discover -s scripts/tests -t scripts
|
|
||||||
|
|
||||||
# What the validator below cannot see. It loads the tree the way the slicer does, so
|
|
||||||
# it never notices a profile no <vendor>.json indexes, a preset name two files claim,
|
|
||||||
# an id that is not the mint of its own triple, or a file that normalize and
|
|
||||||
# update-index would still rewrite.
|
|
||||||
# The step id is the handle the PR comment and the failure gate below use; renaming it
|
|
||||||
# silently disables them.
|
|
||||||
- name: Check profiles (orca_profile_tool.py)
|
|
||||||
id: profile_tool
|
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
set +e
|
set +e
|
||||||
python3 ./scripts/orca_profile_tool.py check 2>&1 | tee ${{ runner.temp }}/profile_tool.log
|
python3 ./scripts/orca_extra_profile_check.py 2>&1 | tee ${{ runner.temp }}/extra_json_check.log
|
||||||
exit ${PIPESTATUS[0]}
|
exit ${PIPESTATUS[0]}
|
||||||
|
|
||||||
# download
|
# download
|
||||||
- name: Download
|
- name: Download
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: |
|
run: |
|
||||||
curl -L -o OrcaSlicer_profile_validator https://github.com/OrcaSlicer/OrcaSlicer/releases/download/nightly-builds/OrcaSlicer_profile_validator_Linux_Ubuntu2404_nightly
|
curl -LJO https://github.com/SoftFever/Orca_tools/releases/download/1/OrcaSlicer_profile_validator
|
||||||
chmod +x ./OrcaSlicer_profile_validator
|
chmod +x ./OrcaSlicer_profile_validator
|
||||||
|
|
||||||
# Validate all system profiles.
|
# validate profiles
|
||||||
- name: validate system profiles
|
- name: validate system profiles
|
||||||
id: validate_system
|
id: validate_system
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
@@ -71,125 +49,17 @@ jobs:
|
|||||||
set +e
|
set +e
|
||||||
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_system.log
|
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_system.log
|
||||||
exit ${PIPESTATUS[0]}
|
exit ${PIPESTATUS[0]}
|
||||||
# Slice a two-colour cube through every printer so all custom g-code (incl. change_filament_gcode)
|
|
||||||
# is expanded - catches undefined-placeholder / invalid-flow bugs the static checks above cannot see.
|
|
||||||
- name: validate slice (expand custom g-code)
|
|
||||||
id: validate_slice
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
set +e
|
|
||||||
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -s -l 2 2>&1 | tee ${{ runner.temp }}/validate_slice.log
|
|
||||||
exit ${PIPESTATUS[0]}
|
|
||||||
# All vendors' filament_id collisions were fixed, so the duplicate-filament-subtype
|
|
||||||
# check runs tree-wide.
|
|
||||||
- name: validate filament subtype check
|
|
||||||
id: validate_filament_subtypes
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
set +e
|
|
||||||
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 -f 2>&1 | tee ${{ runner.temp }}/validate_filament_subtypes.log
|
|
||||||
exit ${PIPESTATUS[0]}
|
|
||||||
|
|
||||||
- name: validate custom presets
|
- name: validate custom presets
|
||||||
id: validate_custom
|
id: validate_custom
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
working-directory: ${{ github.workspace }}
|
working-directory: ${{ github.workspace }}
|
||||||
run: |
|
run: |
|
||||||
fixtures_dir="${{ runner.temp }}/profile-fixtures"
|
set +e
|
||||||
output_dir="${{ runner.temp }}/custom-preset-validation"
|
curl -LJO https://github.com/OrcaSlicer/OrcaSlicer/releases/download/nightly-builds/orca_custom_preset_tests.zip
|
||||||
combined_log="${{ runner.temp }}/validate_custom.log"
|
unzip -q ./orca_custom_preset_tests.zip -d ${{ github.workspace }}/resources/profiles
|
||||||
summary="${output_dir}/summary.md"
|
./OrcaSlicer_profile_validator -p ${{ github.workspace }}/resources/profiles -l 2 2>&1 | tee ${{ runner.temp }}/validate_custom.log
|
||||||
release_url="https://github.com/OrcaSlicer/OrcaSlicer-profile-validator/releases/download/fixture-archive"
|
exit ${PIPESTATUS[0]}
|
||||||
|
|
||||||
rm -rf "${fixtures_dir}" "${output_dir}"
|
|
||||||
mkdir -p "${fixtures_dir}" "${output_dir}"
|
|
||||||
|
|
||||||
curl -fsSL -o "${fixtures_dir}/manifest.json" "${release_url}/manifest.json"
|
|
||||||
|
|
||||||
MANIFEST_PATH="${fixtures_dir}/manifest.json" python3 <<'PY' > "${fixtures_dir}/fixtures.tsv"
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
|
|
||||||
with open(os.environ["MANIFEST_PATH"], encoding="utf-8") as fh:
|
|
||||||
manifest = json.load(fh)
|
|
||||||
|
|
||||||
if isinstance(manifest, dict):
|
|
||||||
entries = manifest.get("fixtures", [])
|
|
||||||
else:
|
|
||||||
entries = manifest
|
|
||||||
|
|
||||||
for entry in entries:
|
|
||||||
version = entry.get("version", "")
|
|
||||||
asset = entry.get("asset", "")
|
|
||||||
sha256 = entry.get("asset_sha256", "")
|
|
||||||
if not version or not asset:
|
|
||||||
continue
|
|
||||||
print(f"{version}\t{asset}\t{sha256}")
|
|
||||||
PY
|
|
||||||
|
|
||||||
if [ ! -s "${fixtures_dir}/fixtures.tsv" ]; then
|
|
||||||
echo "No custom preset fixtures found in ${release_url}/manifest.json" | tee "${combined_log}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
{
|
|
||||||
echo "## Custom Preset Fixture Validation"
|
|
||||||
echo ""
|
|
||||||
echo "| Version | Status | Log |"
|
|
||||||
echo "| --- | --- | --- |"
|
|
||||||
} > "${summary}"
|
|
||||||
|
|
||||||
status=0
|
|
||||||
failed_logs=()
|
|
||||||
|
|
||||||
while IFS=$'\t' read -r version asset expected_sha256; do
|
|
||||||
fixture_zip="${fixtures_dir}/${asset}"
|
|
||||||
asset_url_name="$(python3 -c 'import sys, urllib.parse; print(urllib.parse.quote(sys.argv[1], safe=""))' "${asset}")"
|
|
||||||
profile_tree="${output_dir}/profiles-${version}"
|
|
||||||
log_path="${output_dir}/${version}.log"
|
|
||||||
|
|
||||||
curl -fsSL -o "${fixture_zip}" "${release_url}/${asset_url_name}"
|
|
||||||
|
|
||||||
if [ -n "${expected_sha256}" ] && [ "${expected_sha256}" != "<sha256>" ]; then
|
|
||||||
echo "${expected_sha256} ${fixture_zip}" | sha256sum -c -
|
|
||||||
fi
|
|
||||||
|
|
||||||
rm -rf "${profile_tree}"
|
|
||||||
mkdir -p "${profile_tree}"
|
|
||||||
cp -a "${{ github.workspace }}/resources/profiles/." "${profile_tree}/"
|
|
||||||
rm -rf "${profile_tree}/user"
|
|
||||||
unzip -q "${fixture_zip}" -d "${profile_tree}"
|
|
||||||
|
|
||||||
set +e
|
|
||||||
./OrcaSlicer_profile_validator -p "${profile_tree}" -l 2 > "${log_path}" 2>&1
|
|
||||||
result=$?
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ "${result}" -eq 0 ]; then
|
|
||||||
echo "| ${version} | PASS | ${version}.log |" >> "${summary}"
|
|
||||||
else
|
|
||||||
echo "| ${version} | FAIL | ${version}.log |" >> "${summary}"
|
|
||||||
failed_logs+=("${log_path}")
|
|
||||||
status=1
|
|
||||||
fi
|
|
||||||
done < "${fixtures_dir}/fixtures.tsv"
|
|
||||||
|
|
||||||
{
|
|
||||||
cat "${summary}"
|
|
||||||
if [ "${#failed_logs[@]}" -gt 0 ]; then
|
|
||||||
echo ""
|
|
||||||
echo "## Failed Fixture Logs"
|
|
||||||
for log_path in "${failed_logs[@]}"; do
|
|
||||||
echo ""
|
|
||||||
echo "### $(basename "${log_path}" .log)"
|
|
||||||
echo '```'
|
|
||||||
head -c 12000 "${log_path}" || echo "No output captured"
|
|
||||||
echo '```'
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
} | tee "${combined_log}"
|
|
||||||
|
|
||||||
exit "${status}"
|
|
||||||
|
|
||||||
- name: Prepare PR number for comment workflow
|
- name: Prepare PR number for comment workflow
|
||||||
if: ${{ always() && github.event_name == 'pull_request' }}
|
if: ${{ always() && github.event_name == 'pull_request' }}
|
||||||
@@ -198,7 +68,7 @@ jobs:
|
|||||||
echo "${{ github.event.pull_request.number }}" > ${{ runner.temp }}/profile-check-results/pr_number.txt
|
echo "${{ github.event.pull_request.number }}" > ${{ runner.temp }}/profile-check-results/pr_number.txt
|
||||||
|
|
||||||
- name: Prepare comment artifact
|
- name: Prepare comment artifact
|
||||||
if: ${{ always() && github.event_name == 'pull_request' && (steps.profile_tool.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_slice.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
if: ${{ always() && github.event_name == 'pull_request' && (steps.extra_json_check.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||||
run: |
|
run: |
|
||||||
{
|
{
|
||||||
# Marker matched by check_profiles_comment.yml to delete prior comments.
|
# Marker matched by check_profiles_comment.yml to delete prior comments.
|
||||||
@@ -206,11 +76,11 @@ jobs:
|
|||||||
echo "## :x: Profile Validation Errors"
|
echo "## :x: Profile Validation Errors"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [ "${{ steps.profile_tool.outcome }}" = "failure" ]; then
|
if [ "${{ steps.extra_json_check.outcome }}" = "failure" ]; then
|
||||||
echo "### Profile Check Failed (orca_profile_tool.py)"
|
echo "### Extra JSON Check Failed"
|
||||||
echo ""
|
echo ""
|
||||||
echo '```'
|
echo '```'
|
||||||
head -c 30000 ${{ runner.temp }}/profile_tool.log || echo "No output captured"
|
head -c 30000 ${{ runner.temp }}/extra_json_check.log || echo "No output captured"
|
||||||
echo '```'
|
echo '```'
|
||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
@@ -224,24 +94,6 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${{ steps.validate_slice.outcome }}" = "failure" ]; then
|
|
||||||
echo "### Slice Validation Failed (custom g-code expansion)"
|
|
||||||
echo ""
|
|
||||||
echo '```'
|
|
||||||
head -c 30000 ${{ runner.temp }}/validate_slice.log || echo "No output captured"
|
|
||||||
echo '```'
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${{ steps.validate_filament_subtypes.outcome }}" = "failure" ]; then
|
|
||||||
echo "### Filament Subtype Validation Failed"
|
|
||||||
echo ""
|
|
||||||
echo '```'
|
|
||||||
head -c 30000 ${{ runner.temp }}/validate_filament_subtypes.log || echo "No output captured"
|
|
||||||
echo '```'
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "${{ steps.validate_custom.outcome }}" = "failure" ]; then
|
if [ "${{ steps.validate_custom.outcome }}" = "failure" ]; then
|
||||||
echo "### Custom Preset Validation Failed"
|
echo "### Custom Preset Validation Failed"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -252,7 +104,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "---"
|
echo "---"
|
||||||
echo '*Fix the errors above and push a new commit. To reproduce this run locally: `scripts/check_profile.sh`, or `scripts\check_profile.bat` on Windows.*'
|
echo "*Please fix the above errors and push a new commit.*"
|
||||||
} > ${{ runner.temp }}/profile-check-results/pr_comment.md
|
} > ${{ runner.temp }}/profile-check-results/pr_comment.md
|
||||||
|
|
||||||
- name: Upload comment artifact
|
- name: Upload comment artifact
|
||||||
@@ -264,8 +116,7 @@ jobs:
|
|||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
- name: Fail if any check failed
|
- name: Fail if any check failed
|
||||||
if: ${{ always() && (steps.profile_tool.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_slice.outcome == 'failure' || steps.validate_filament_subtypes.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
if: ${{ always() && (steps.extra_json_check.outcome == 'failure' || steps.validate_system.outcome == 'failure' || steps.validate_custom.outcome == 'failure') }}
|
||||||
run: |
|
run: |
|
||||||
echo "One or more profile checks failed; see the step logs above."
|
echo "One or more profile checks failed. See above for details."
|
||||||
echo 'Reproduce the whole run locally with scripts/check_profile.sh (scripts\check_profile.bat on Windows).'
|
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Run Claude Code slash command
|
- name: Run Claude Code slash command
|
||||||
uses: anthropics/claude-code-base-action@beta
|
uses: anthropics/claude-code-base-action@beta
|
||||||
with:
|
with:
|
||||||
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
|
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
|
||||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||||
model: "claude-sonnet-4-5-20250929"
|
claude_args: "--model claude-sonnet-4-5-20250929"
|
||||||
claude_env: |
|
claude_env: |
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
|||||||
@@ -19,21 +19,14 @@ jobs:
|
|||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
# Doxygen with call graphs over all of src/ outgrows the runner's RAM;
|
- uses: thejerrybao/setup-swap-space@v1
|
||||||
# replace the runner's swapfile with an 8 GB one.
|
with:
|
||||||
- name: Grow swap space
|
swap-space-path: /swapfile
|
||||||
run: |
|
swap-size-gb: 8
|
||||||
set -euo pipefail
|
remove-existing-swap-files: true
|
||||||
sudo swapoff -a
|
|
||||||
sudo rm -f /swapfile
|
|
||||||
sudo fallocate -l 8G /swapfile
|
|
||||||
sudo chmod 600 /swapfile
|
|
||||||
sudo mkswap /swapfile
|
|
||||||
sudo swapon /swapfile
|
|
||||||
free -h
|
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Install Doxygen and Graphviz
|
- name: Install Doxygen and Graphviz
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -1,219 +0,0 @@
|
|||||||
# Nightly parity checks from OrcaSlicer/orca-test-repo, kept out of the
|
|
||||||
# per-build "Run external slicer regression tests" step because they take far
|
|
||||||
# longer than that step's budget:
|
|
||||||
# effect - the CLI override sweep's full effect stage: every landed option
|
|
||||||
# re-sliced on its own to see whether it changes the G-code
|
|
||||||
# harness - the GUI-vs-CLI parity harness (metrics only, never fails)
|
|
||||||
# Both test the latest successful build_all.yml Linux AppImage from main, with
|
|
||||||
# sources checked out at the commit that build was made from. Nothing here
|
|
||||||
# gates a build or a PR.
|
|
||||||
name: Parity Nightly
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# build_all.yml starts at 17:00 UTC and has finished by ~20:00
|
|
||||||
- cron: "0 21 * * *"
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
test_repo_ref:
|
|
||||||
description: "orca-test-repo ref to run"
|
|
||||||
required: false
|
|
||||||
default: "main"
|
|
||||||
build_branch:
|
|
||||||
description: "branch whose latest successful build_all artifact to test"
|
|
||||||
required: false
|
|
||||||
default: "main"
|
|
||||||
fixtures:
|
|
||||||
description: "harness fixture ids, space-separated (empty = all)"
|
|
||||||
required: false
|
|
||||||
default: ""
|
|
||||||
cli_presets:
|
|
||||||
description: "harness lane C presets: flat = flatten inherits first, raw = leaf profile as-is"
|
|
||||||
required: false
|
|
||||||
default: "flat"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
actions: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Find the build to test
|
|
||||||
# Don't run scheduled checks on forks
|
|
||||||
if: github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer'
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
outputs:
|
|
||||||
run_id: ${{ steps.find.outputs.run_id }}
|
|
||||||
head_sha: ${{ steps.find.outputs.head_sha }}
|
|
||||||
steps:
|
|
||||||
- id: find
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
gh run list --workflow build_all.yml \
|
|
||||||
--branch "${{ inputs.build_branch || 'main' }}" \
|
|
||||||
--status success --limit 1 --json databaseId,headSha \
|
|
||||||
--jq '"run_id=\(.[0].databaseId)\nhead_sha=\(.[0].headSha)"' \
|
|
||||||
>> "$GITHUB_OUTPUT"
|
|
||||||
cat "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
effect:
|
|
||||||
name: Override sweep effect stage (shard ${{ matrix.shard }})
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
timeout-minutes: 60
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
# orca-test-repo's parity/effect_routing.json holds a 2-way split,
|
|
||||||
# ~12.5 min a shard on this runner
|
|
||||||
shard: [0, 1]
|
|
||||||
steps:
|
|
||||||
- &checkout-suite
|
|
||||||
name: Check out the test suite
|
|
||||||
uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
repository: OrcaSlicer/orca-test-repo
|
|
||||||
ref: ${{ inputs.test_repo_ref || 'main' }}
|
|
||||||
path: orca-test-repo
|
|
||||||
|
|
||||||
# The AppImage ships only packed preset caches, so profiles and the CLI
|
|
||||||
# option surface come from the sources the build was made from
|
|
||||||
- &checkout-slicer
|
|
||||||
name: Check out OrcaSlicer at the build's commit
|
|
||||||
uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
ref: ${{ needs.build.outputs.head_sha }}
|
|
||||||
path: slicer
|
|
||||||
lfs: 'false'
|
|
||||||
|
|
||||||
- &extract-appimage
|
|
||||||
name: Download and extract the Linux AppImage
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
gh run download "${{ needs.build.outputs.run_id }}" --dir appimage \
|
|
||||||
--pattern "OrcaSlicer_Linux_ubuntu_2404*"
|
|
||||||
appimage=$(find appimage -name "*.AppImage" ! -name "*aarch64*" | head -1)
|
|
||||||
[ -n "$appimage" ] || { echo "no x86_64 AppImage in run ${{ needs.build.outputs.run_id }}"; exit 1; }
|
|
||||||
chmod +x "$appimage"
|
|
||||||
"$appimage" --appimage-extract > /dev/null
|
|
||||||
# The bare binary cannot find the AppImage's bundled libraries; AppRun
|
|
||||||
# sets them up and execs it, so exit codes and signals pass through
|
|
||||||
[ -x squashfs-root/AppRun ] || { echo "no AppRun in the AppImage"; exit 1; }
|
|
||||||
echo "ORCA_BIN=$PWD/squashfs-root/AppRun" >> "$GITHUB_ENV"
|
|
||||||
echo "ORCA_SOURCE=$PWD/slicer" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
- name: Install the AppImage's host runtime dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y --no-install-recommends \
|
|
||||||
libopengl0 libglu1-mesa libgl1 libegl1 libwebkit2gtk-4.1-0
|
|
||||||
|
|
||||||
- uses: actions/setup-python@v6
|
|
||||||
with:
|
|
||||||
python-version: "3.12"
|
|
||||||
|
|
||||||
- name: Install suite dependencies
|
|
||||||
run: pip install -r orca-test-repo/requirements.txt
|
|
||||||
|
|
||||||
- name: Run the override sweep with the full effect stage
|
|
||||||
id: run
|
|
||||||
continue-on-error: true
|
|
||||||
working-directory: orca-test-repo
|
|
||||||
run: |
|
|
||||||
set -o pipefail
|
|
||||||
# -rA keeps the per-stage summaries, which pytest otherwise swallows
|
|
||||||
# for passing tests
|
|
||||||
python -m pytest test_cli_overrides.py -c pytest.ini -v -rA \
|
|
||||||
--effect-full --effect-shard ${{ matrix.shard }}/2 \
|
|
||||||
--orca-bin "$ORCA_BIN" --orca-source "$ORCA_SOURCE" \
|
|
||||||
2>&1 | tee ../sweep.log
|
|
||||||
|
|
||||||
- name: Publish job summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
{
|
|
||||||
echo "## Override sweep effect stage, shard ${{ matrix.shard }}/2"
|
|
||||||
echo "Build ${{ needs.build.outputs.head_sha }} (run ${{ needs.build.outputs.run_id }})"
|
|
||||||
echo '```'
|
|
||||||
grep -E "\[override sweep" sweep.log || echo "no stage summaries, see the log"
|
|
||||||
grep -E "^=+ .*(passed|failed)" sweep.log | tail -1 || true
|
|
||||||
echo '```'
|
|
||||||
} >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
|
|
||||||
- name: Upload the override report
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: override-report-shard${{ matrix.shard }}
|
|
||||||
path: |
|
|
||||||
orca-test-repo/.pytest_cache/override_report.json
|
|
||||||
sweep.log
|
|
||||||
if-no-files-found: warn
|
|
||||||
retention-days: 30
|
|
||||||
|
|
||||||
# The sweep step continues on error so the summary and report still get
|
|
||||||
# published; this puts the failure back on the job
|
|
||||||
- name: Fail the job if the sweep failed
|
|
||||||
if: steps.run.outcome == 'failure'
|
|
||||||
run: |
|
|
||||||
echo "the override sweep failed, see the job summary and the uploaded report" >&2
|
|
||||||
exit 1
|
|
||||||
|
|
||||||
harness:
|
|
||||||
name: GUI-vs-CLI parity harness
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
timeout-minutes: 180
|
|
||||||
steps:
|
|
||||||
- *checkout-suite
|
|
||||||
- *checkout-slicer
|
|
||||||
- *extract-appimage
|
|
||||||
|
|
||||||
- name: Install display tooling and the AppImage's host runtime
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y --no-install-recommends \
|
|
||||||
xvfb xdotool imagemagick openbox mesa-utils \
|
|
||||||
libopengl0 libglu1-mesa libgl1 libegl1 libwebkit2gtk-4.1-0
|
|
||||||
|
|
||||||
- name: Run the parity harness
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
fixtures=()
|
|
||||||
for f in ${{ inputs.fixtures || '' }}; do
|
|
||||||
fixtures+=(--fixture "$f")
|
|
||||||
done
|
|
||||||
# 2 GUI displays: ~1.5 cores peak / ~1.9 GB on this 4-vCPU runner,
|
|
||||||
# and each fixture is fully isolated, so results match a serial run
|
|
||||||
python3 orca-test-repo/parity/run_parity.py \
|
|
||||||
--slicer-root "$ORCA_SOURCE" --bin "$ORCA_BIN" \
|
|
||||||
--cli-presets "${{ inputs.cli_presets || 'flat' }}" \
|
|
||||||
--gui-workers 2 --out "$PWD/parity-out" "${fixtures[@]}"
|
|
||||||
|
|
||||||
- name: Publish job summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
if [ -f parity-out/report.md ]; then
|
|
||||||
cat parity-out/report.md >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
else
|
|
||||||
echo "the harness produced no report, see the log" >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Drop per-lane datadirs before upload
|
|
||||||
if: always()
|
|
||||||
run: rm -rf parity-out/*/seed parity-out/*/datadir-* || true
|
|
||||||
|
|
||||||
- name: Upload the scorecard and evidence
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: parity-scorecard
|
|
||||||
path: parity-out/
|
|
||||||
if-no-files-found: warn
|
|
||||||
retention-days: 30
|
|
||||||
@@ -24,7 +24,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Ask PR author for label
|
- name: Ask PR author for label
|
||||||
uses: actions/github-script@v9
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
function isPermissionDenied(error) {
|
function isPermissionDenied(error) {
|
||||||
@@ -32,21 +32,13 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const allowedLabels = [
|
const allowedLabels = [
|
||||||
// kind of change
|
|
||||||
'crash',
|
|
||||||
'bug-fix',
|
'bug-fix',
|
||||||
'enhancement',
|
'enhancement',
|
||||||
'QoL',
|
|
||||||
'optimization',
|
|
||||||
// area
|
|
||||||
'UI/UX',
|
|
||||||
'profile',
|
|
||||||
'Localization',
|
'Localization',
|
||||||
// infrastructure
|
'profile',
|
||||||
'build',
|
'QoL',
|
||||||
'test',
|
'UI/UX',
|
||||||
'dependencies',
|
'dependencies'
|
||||||
'documentation'
|
|
||||||
];
|
];
|
||||||
const pr = context.payload.pull_request;
|
const pr = context.payload.pull_request;
|
||||||
const labelsList = `${allowedLabels
|
const labelsList = `${allowedLabels
|
||||||
@@ -87,92 +79,6 @@ jobs:
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
localization-pr:
|
|
||||||
if: github.event_name == 'pull_request_target'
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
issues: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Auto-label and remind about the localization glossary
|
|
||||||
uses: actions/github-script@v9
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
function isPermissionDenied(error) {
|
|
||||||
return error && error.status === 403 && /Resource not accessible by integration/i.test(error.message || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
const pr = context.payload.pull_request;
|
|
||||||
|
|
||||||
// List changed files once (mirrors the `localization/**` paths filter in check_locale.yml)
|
|
||||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
pull_number: pr.number,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
const touchesLocalization = files.some((file) => file.filename.startsWith('localization/'));
|
|
||||||
const onlyPoFiles = files.length > 0 && files.every((file) => file.filename.endsWith('.po'));
|
|
||||||
|
|
||||||
// If the PR changes only .po files, automatically apply the Localization label
|
|
||||||
if (onlyPoFiles) {
|
|
||||||
try {
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
labels: ['Localization']
|
|
||||||
});
|
|
||||||
core.info('Applied Localization label (PR changes only .po files).');
|
|
||||||
} catch (error) {
|
|
||||||
if (isPermissionDenied(error)) {
|
|
||||||
core.warning('Cannot add Localization label because token cannot write.');
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!touchesLocalization) {
|
|
||||||
core.info('No localization changes detected; skipping glossary reminder.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid posting the reminder twice (e.g. on reopen)
|
|
||||||
const marker = '<!-- localization-glossary-reminder -->';
|
|
||||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
if (comments.some((comment) => (comment.body || '').includes(marker))) {
|
|
||||||
core.info('Glossary reminder already present; skipping.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
body:
|
|
||||||
`${marker}\n` +
|
|
||||||
`Hi @${pr.user.login}, this PR changes translations (\`localization/**\`).\n\n` +
|
|
||||||
`Please make sure recurring terms follow the [Localization glossary](https://www.orcaslicer.com/wiki/localization_glossary), ` +
|
|
||||||
`so the same English term is always rendered the same way within a language and terms that must stay in English ` +
|
|
||||||
`(brand/product names, acronyms, file formats, G-code, macros/variables) are not translated.`
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
if (isPermissionDenied(error)) {
|
|
||||||
core.warning('Skipping glossary reminder because token cannot write comments.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
apply-label:
|
apply-label:
|
||||||
if: github.event_name == 'issue_comment'
|
if: github.event_name == 'issue_comment'
|
||||||
permissions:
|
permissions:
|
||||||
@@ -182,7 +88,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Apply label command from PR author
|
- name: Apply label command from PR author
|
||||||
uses: actions/github-script@v9
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
function isPermissionDenied(error) {
|
function isPermissionDenied(error) {
|
||||||
@@ -190,21 +96,13 @@ jobs:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const allowedLabels = [
|
const allowedLabels = [
|
||||||
// kind of change
|
|
||||||
'crash',
|
|
||||||
'bug-fix',
|
'bug-fix',
|
||||||
'enhancement',
|
'enhancement',
|
||||||
'QoL',
|
|
||||||
'optimization',
|
|
||||||
// area
|
|
||||||
'UI/UX',
|
|
||||||
'profile',
|
|
||||||
'Localization',
|
'Localization',
|
||||||
// infrastructure
|
'profile',
|
||||||
'build',
|
'QoL',
|
||||||
'test',
|
'UI/UX',
|
||||||
'dependencies',
|
'dependencies'
|
||||||
'documentation'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const issue = context.payload.issue;
|
const issue = context.payload.issue;
|
||||||
|
|||||||
@@ -1,911 +0,0 @@
|
|||||||
name: PR Merge Bot
|
|
||||||
|
|
||||||
# Merges a pull request on request from a delegated vendor profile maintainer.
|
|
||||||
# The merge is performed by this workflow's GITHUB_TOKEN, so a delegate needs no
|
|
||||||
# repository access.
|
|
||||||
#
|
|
||||||
# Commands, posted as a comment on the PR:
|
|
||||||
# /bot merge squash-merge the PR
|
|
||||||
# /bot merge --dry-run report the verdict without merging
|
|
||||||
#
|
|
||||||
# Merges only when the commenter holds a grant covering every changed path, the
|
|
||||||
# PR targets main or release/*, and CI is green on the head commit. Otherwise it
|
|
||||||
# comments naming the files that fell outside the grant.
|
|
||||||
#
|
|
||||||
# When a PR touching resources/profiles/** is opened, two labels are applied
|
|
||||||
# independently of the merge command:
|
|
||||||
# profile every changed path is inside resources/profiles/
|
|
||||||
# orca profile partner the PR author holds a grant covering every changed
|
|
||||||
# path, plus a one-time comment explaining /bot merge
|
|
||||||
# Neither label changes what the merge command checks.
|
|
||||||
#
|
|
||||||
# Grants come from the FOLDER_MERGERS variable in the `merge-delegation`
|
|
||||||
# environment: one per line, `account: path`, `#` comments and blank lines
|
|
||||||
# allowed. Paths may contain spaces. A vendor takes two grants, the folder and
|
|
||||||
# its sibling bundle JSON:
|
|
||||||
#
|
|
||||||
# # Acme profiles
|
|
||||||
# vendor-maintainer: resources/profiles/Acme/
|
|
||||||
# vendor-maintainer: resources/profiles/Acme.json
|
|
||||||
#
|
|
||||||
# Edit the grant list (environment scope, so admin only):
|
|
||||||
# gh variable set FOLDER_MERGERS --env merge-delegation --body "$(cat folder-mergers.txt)"
|
|
||||||
# gh variable get FOLDER_MERGERS --env merge-delegation
|
|
||||||
#
|
|
||||||
# Stop all merging without touching this file:
|
|
||||||
# gh variable set MERGE_BOT_DRY_RUN --body true
|
|
||||||
|
|
||||||
on:
|
|
||||||
issue_comment:
|
|
||||||
types:
|
|
||||||
- created
|
|
||||||
# Labels profile PRs on open, without waiting for a /bot merge command.
|
|
||||||
pull_request_target:
|
|
||||||
types:
|
|
||||||
- opened
|
|
||||||
paths:
|
|
||||||
- 'resources/profiles/**'
|
|
||||||
|
|
||||||
# One merge attempt per PR at a time, so two quick comments cannot race.
|
|
||||||
# Labels run under their own group, so a queued label run is not replaced by
|
|
||||||
# a merge run for the same PR.
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.issue.number || github.event.pull_request.number }}
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
merge:
|
|
||||||
# Skips the job unless a PR comment mentions the command.
|
|
||||||
if: >-
|
|
||||||
github.repository == 'OrcaSlicer/OrcaSlicer'
|
|
||||||
&& github.event_name == 'issue_comment'
|
|
||||||
&& github.event.issue.pull_request != null
|
|
||||||
&& contains(github.event.comment.body, '/bot merge')
|
|
||||||
permissions:
|
|
||||||
contents: write # pulls.merge
|
|
||||||
pull-requests: write # pulls.merge
|
|
||||||
issues: write # feedback comment + reactions
|
|
||||||
actions: write # re-dispatch build_all.yml after the merge
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
# Supplies FOLDER_MERGERS. Must carry no protection rules, or every
|
|
||||||
# delegated merge and partner label run would wait for a human reviewer.
|
|
||||||
environment: merge-delegation
|
|
||||||
steps:
|
|
||||||
- name: Merge PR on behalf of a folder delegate
|
|
||||||
uses: actions/github-script@v9
|
|
||||||
env:
|
|
||||||
# Read as env vars, never interpolated into the script body.
|
|
||||||
FOLDER_MERGERS: ${{ vars.FOLDER_MERGERS }}
|
|
||||||
MERGE_BOT_DRY_RUN: ${{ vars.MERGE_BOT_DRY_RUN }}
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
function isPermissionDenied(error) {
|
|
||||||
return error && error.status === 403 && /Resource not accessible by integration/i.test(error.message || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
|
|
||||||
const MARKER = '<!-- pr-merge-bot -->';
|
|
||||||
// No grant may reach outside this root.
|
|
||||||
const DELEGATABLE_ROOT = 'resources/profiles/';
|
|
||||||
const ALLOWED_BASE_BRANCH = /^(?:main|release\/.+)$/;
|
|
||||||
const MERGE_METHOD = 'squash';
|
|
||||||
const REQUIRED_CHECK = 'Check profiles'; // job name in check_profiles.yml
|
|
||||||
const LISTFILES_CAP = 3000;
|
|
||||||
const MAX_REPORTED_FILES = 12;
|
|
||||||
const MERGEABLE_ATTEMPTS = 5;
|
|
||||||
const MERGEABLE_DELAY_MS = 2000;
|
|
||||||
const OK_CONCLUSIONS = new Set(['success', 'neutral', 'skipped']);
|
|
||||||
const REGULAR_FILE_MODES = new Set(['100644', '100755']);
|
|
||||||
|
|
||||||
// Paths refused whatever the grants say. Checked before grants, so
|
|
||||||
// delegating a new root means removing it from this list too.
|
|
||||||
const DENIED_PATTERNS = [
|
|
||||||
/^\.github\//,
|
|
||||||
/(^|\/)\.git(attributes|modules|ignore|config)$/,
|
|
||||||
/^(?:src|deps|deps_src|tests|tools|cmake|sandboxes|scripts|docs?|localization|bbl)\//,
|
|
||||||
/(^|\/)cmakelists\.txt$/,
|
|
||||||
/\.cmake$/,
|
|
||||||
/^build_[^/]*\.(?:sh|bat)$/,
|
|
||||||
/^version\.inc$/,
|
|
||||||
// Executables, including those inside the delegatable root.
|
|
||||||
/\.(?:sh|bash|bat|cmd|ps1|py|js|mjs|cjs|ts|rb|pl|php)$/
|
|
||||||
];
|
|
||||||
|
|
||||||
function parseGrants(raw) {
|
|
||||||
// GitHub login: 1-39 chars, alphanumerics with single interior hyphens.
|
|
||||||
const loginPattern = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/;
|
|
||||||
const grantsByLogin = new Map();
|
|
||||||
const problems = [];
|
|
||||||
|
|
||||||
(raw || '').split(/\r?\n/).forEach((rawLine, index) => {
|
|
||||||
const line = rawLine.trim();
|
|
||||||
if (!line || line.startsWith('#')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Splits on the first colon only, so paths may contain ':' and spaces.
|
|
||||||
const separator = line.indexOf(':');
|
|
||||||
if (separator === -1) {
|
|
||||||
problems.push(`line ${index + 1}: expected \`account: path\``);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const login = line.slice(0, separator).trim().replace(/^@/, '');
|
|
||||||
const path = line.slice(separator + 1).trim().replace(/\/+$/, '');
|
|
||||||
|
|
||||||
if (!loginPattern.test(login)) {
|
|
||||||
problems.push(`line ${index + 1}: \`${login}\` is not a valid GitHub account name`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (/[\\*?\u0000-\u001f\u007f]/.test(path) || path.split('/').includes('..') || path.includes('//')) {
|
|
||||||
problems.push(`line ${index + 1}: invalid path (no globs, \`..\`, \`//\`, backslashes or control characters)`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Rejects anything outside the root, and the bare root itself.
|
|
||||||
if (!path.startsWith(DELEGATABLE_ROOT) || path.length <= DELEGATABLE_ROOT.length) {
|
|
||||||
problems.push(`line ${index + 1}: \`${path}\` is not inside \`${DELEGATABLE_ROOT}\``);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = login.toLowerCase();
|
|
||||||
grantsByLogin.set(key, (grantsByLogin.get(key) || []).concat(path));
|
|
||||||
});
|
|
||||||
|
|
||||||
return { grantsByLogin, problems };
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDenied(path) {
|
|
||||||
if (/[\\\u0000-\u001f\u007f]/.test(path) || path.startsWith('/') || path.split('/').includes('..')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = path.normalize('NFKC').toLowerCase();
|
|
||||||
return DENIED_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Byte-exact match on directory boundaries, so a grant of
|
|
||||||
// `.../Acme` covers neither `.../Acme Labs/x.json` nor `.../Acme.json`.
|
|
||||||
function isGranted(path, grants) {
|
|
||||||
return grants.some((grant) => path === grant || path.startsWith(`${grant}/`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both endpoints of a rename; both must satisfy the grant.
|
|
||||||
function pathsFor(file) {
|
|
||||||
return [file.filename, file.previous_filename].filter(Boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatList(items) {
|
|
||||||
const unique = [...new Set(items)];
|
|
||||||
const shown = unique.slice(0, MAX_REPORTED_FILES).map((item) => `- \`${item}\``);
|
|
||||||
if (unique.length > MAX_REPORTED_FILES) {
|
|
||||||
shown.push(`- …and ${unique.length - MAX_REPORTED_FILES} more`);
|
|
||||||
}
|
|
||||||
return shown.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
const { owner, repo } = context.repo;
|
|
||||||
const issue = context.payload.issue;
|
|
||||||
const comment = context.payload.comment;
|
|
||||||
|
|
||||||
if (!issue.pull_request) {
|
|
||||||
core.info('Ignoring comment that is not on a pull request.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Ignores a comment whose sender is not its author.
|
|
||||||
if (context.payload.action !== 'created' || context.payload.sender.login !== comment.user.login) {
|
|
||||||
core.warning('Ignoring comment whose sender does not match its author.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (comment.user.type !== 'User') {
|
|
||||||
core.info('Ignoring bot-authored command.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const commandLine = (comment.body || '')
|
|
||||||
.split('\n')
|
|
||||||
.map((line) => line.trim())
|
|
||||||
.find((line) => /^\/bot\s+merge\b/i.test(line));
|
|
||||||
|
|
||||||
if (!commandLine) {
|
|
||||||
core.info('No /bot merge command found.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const commenter = comment.user.login;
|
|
||||||
const { grantsByLogin, problems } = parseGrants(process.env.FOLDER_MERGERS);
|
|
||||||
const grants = grantsByLogin.get(commenter.toLowerCase()) || [];
|
|
||||||
|
|
||||||
for (const problem of problems) {
|
|
||||||
core.warning(`FOLDER_MERGERS ${problem}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Says nothing to accounts with no grant, so it cannot be used to spam.
|
|
||||||
if (!grants.length) {
|
|
||||||
core.info(`Ignoring /bot merge from @${commenter}: not listed in FOLDER_MERGERS.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Warns instead of failing when the token cannot post feedback.
|
|
||||||
async function bestEffort(call, warning) {
|
|
||||||
try {
|
|
||||||
await call();
|
|
||||||
} catch (error) {
|
|
||||||
if (isPermissionDenied(error)) {
|
|
||||||
core.warning(warning);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const react = (content) => bestEffort(
|
|
||||||
() => github.rest.reactions.createForIssueComment({ owner, repo, comment_id: comment.id, content }),
|
|
||||||
`Cannot add the "${content}" reaction because the token cannot write.`);
|
|
||||||
|
|
||||||
const say = (body) => bestEffort(
|
|
||||||
() => github.rest.issues.createComment({ owner, repo, issue_number: issue.number, body: `${MARKER}\n${body}` }),
|
|
||||||
'Cannot post a comment because the token cannot write comments.');
|
|
||||||
|
|
||||||
// Declines the command: warns in the log, reacts, explains on the PR.
|
|
||||||
async function refuse(reason) {
|
|
||||||
const configNote = problems.length
|
|
||||||
? `\n\n\`FOLDER_MERGERS\` also has problems a maintainer needs to fix:\n${problems.map((problem) => `- ${problem}`).join('\n')}`
|
|
||||||
: '';
|
|
||||||
const grantsNote = `\n\n<details><summary>Your current grants</summary>\n\n${formatList(grants)}\n\n</details>`;
|
|
||||||
|
|
||||||
core.warning(`Refused /bot merge from @${commenter}: ${reason}`);
|
|
||||||
await react('-1');
|
|
||||||
await say(`@${commenter} I can't merge this PR: ${reason}${configNote}${grantsNote}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await react('eyes');
|
|
||||||
|
|
||||||
const args = (commandLine.match(/^\/bot\s+merge\s*(.*)$/i)[1] || '').trim().split(/\s+/).filter(Boolean);
|
|
||||||
const unknownArgs = args.filter((arg) => arg.toLowerCase() !== '--dry-run');
|
|
||||||
const dryRun = String(process.env.MERGE_BOT_DRY_RUN || '').toLowerCase() === 'true'
|
|
||||||
|| unknownArgs.length !== args.length;
|
|
||||||
|
|
||||||
if (unknownArgs.length) {
|
|
||||||
return refuse(
|
|
||||||
`I don't understand ${unknownArgs.map((arg) => `\`${arg}\``).join(', ')}. ` +
|
|
||||||
'Usage: `/bot merge` or `/bot merge --dry-run`.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refuses everything while the grant list is malformed.
|
|
||||||
if (problems.length) {
|
|
||||||
return refuse(
|
|
||||||
'the `FOLDER_MERGERS` grant list has malformed lines, so I refuse every merge until it is fixed.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let { data: pr } = await github.rest.pulls.get({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
pull_number: issue.number
|
|
||||||
});
|
|
||||||
|
|
||||||
if (pr.merged) {
|
|
||||||
return refuse('it is already merged.');
|
|
||||||
}
|
|
||||||
if (pr.state !== 'open') {
|
|
||||||
return refuse(`its state is \`${pr.state}\`, not \`open\`.`);
|
|
||||||
}
|
|
||||||
if (pr.draft) {
|
|
||||||
return refuse('it is still a draft. Mark it ready for review first.');
|
|
||||||
}
|
|
||||||
if (!ALLOWED_BASE_BRANCH.test(pr.base.ref)) {
|
|
||||||
return refuse(`it targets \`${pr.base.ref}\`. Delegated merges are only allowed into \`main\` and \`release/*\`.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- folder scope ----
|
|
||||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
pull_number: pr.number,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!files.length) {
|
|
||||||
return refuse('it changes no files, so there is nothing to verify or merge.');
|
|
||||||
}
|
|
||||||
// Refuses when the file list is truncated or disagrees with the PR.
|
|
||||||
if (files.length >= LISTFILES_CAP || files.length !== pr.changed_files) {
|
|
||||||
return refuse(
|
|
||||||
`it reports ${pr.changed_files} changed files but the API listed ${files.length}, ` +
|
|
||||||
'so the file list is truncated and I cannot verify the folder scope. A maintainer must merge this one.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const deniedFiles = [];
|
|
||||||
const outsideFiles = [];
|
|
||||||
|
|
||||||
for (const file of files) {
|
|
||||||
for (const path of pathsFor(file)) {
|
|
||||||
if (isDenied(path)) {
|
|
||||||
deniedFiles.push(path);
|
|
||||||
} else if (!isGranted(path, grants)) {
|
|
||||||
outsideFiles.push(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deniedFiles.length) {
|
|
||||||
core.error(`@${commenter} attempted a delegated merge touching protected paths: ${deniedFiles.join(', ')}`);
|
|
||||||
return refuse(
|
|
||||||
'it touches paths that are never delegatable, whatever the grants say ' +
|
|
||||||
`(CI, build, scripts or executable files):\n\n${formatList(deniedFiles)}\n\nA maintainer should look at this before it goes any further.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (outsideFiles.length) {
|
|
||||||
return refuse(
|
|
||||||
`${outsideFiles.length} changed path(s) fall outside your grants:\n\n${formatList(outsideFiles)}\n\n` +
|
|
||||||
'A vendor needs both grants: `resources/profiles/<Vendor>/` **and** `resources/profiles/<Vendor>.json`.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- file modes: rejects symlinks and submodules ----
|
|
||||||
// Fetches the delegatable subtree only; listFiles does not report modes.
|
|
||||||
const headSha = pr.head.sha;
|
|
||||||
const { data: tree } = await github.rest.git.getTree({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
tree_sha: `${headSha}:${DELEGATABLE_ROOT.replace(/\/$/, '')}`,
|
|
||||||
recursive: 'true'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (tree.truncated) {
|
|
||||||
return refuse('the git tree is too large to verify file modes. A maintainer must merge this one.');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entry paths are subtree-relative.
|
|
||||||
const modesByPath = new Map(tree.tree.map((entry) => [`${DELEGATABLE_ROOT}${entry.path}`, entry.mode]));
|
|
||||||
const irregularFiles = files
|
|
||||||
.filter((file) => file.status !== 'removed')
|
|
||||||
.map((file) => [file.filename, modesByPath.get(file.filename)])
|
|
||||||
.filter(([, mode]) => !REGULAR_FILE_MODES.has(mode))
|
|
||||||
.map(([path, mode]) => `${path} (mode ${mode || 'missing'})`);
|
|
||||||
|
|
||||||
if (irregularFiles.length) {
|
|
||||||
core.error(`@${commenter} attempted a delegated merge with non-regular files: ${irregularFiles.join(', ')}`);
|
|
||||||
return refuse(
|
|
||||||
`it adds symlinks, submodules or files I cannot verify:\n\n${formatList(irregularFiles)}\n\nA maintainer should look at this before it goes any further.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- mergeability: waits for GitHub to compute it ----
|
|
||||||
for (let attempt = 0; pr.mergeable === null && attempt < MERGEABLE_ATTEMPTS; attempt += 1) {
|
|
||||||
core.info(`Mergeability not computed yet; retrying in ${MERGEABLE_DELAY_MS}ms.`);
|
|
||||||
await sleep(MERGEABLE_DELAY_MS);
|
|
||||||
({ data: pr } = await github.rest.pulls.get({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
pull_number: pr.number
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pr.mergeable === null) {
|
|
||||||
return refuse('GitHub is still working out whether it can be merged. Try `/bot merge` again in a minute.');
|
|
||||||
}
|
|
||||||
if (!pr.mergeable) {
|
|
||||||
return refuse(`it is not mergeable (\`${pr.mergeable_state}\`) - most likely a conflict with \`${pr.base.ref}\`.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- CI on the head commit ----
|
|
||||||
const checkRuns = await github.paginate(github.rest.checks.listForRef, {
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
ref: headSha,
|
|
||||||
filter: 'latest',
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
const pendingChecks = checkRuns.filter((run) => run.status !== 'completed');
|
|
||||||
const failedChecks = checkRuns.filter((run) => run.status === 'completed' && !OK_CONCLUSIONS.has(run.conclusion));
|
|
||||||
|
|
||||||
if (pendingChecks.length) {
|
|
||||||
return refuse(
|
|
||||||
`${pendingChecks.length} check(s) are still running on \`${headSha.slice(0, 7)}\`:\n\n` +
|
|
||||||
`${formatList(pendingChecks.map((run) => run.name))}\n\nRe-run \`/bot merge\` once they finish.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (failedChecks.length) {
|
|
||||||
return refuse(
|
|
||||||
`${failedChecks.length} check(s) are not green on \`${headSha.slice(0, 7)}\`:\n\n` +
|
|
||||||
formatList(failedChecks.map((run) => `${run.name} (${run.conclusion})`))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data: combined } = await github.rest.repos.getCombinedStatusForRef({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
ref: headSha
|
|
||||||
});
|
|
||||||
// total_count 0 only means there are no legacy statuses.
|
|
||||||
if (combined.total_count > 0 && combined.state !== 'success') {
|
|
||||||
return refuse(
|
|
||||||
`the combined commit status on \`${headSha.slice(0, 7)}\` is \`${combined.state}\`:\n\n` +
|
|
||||||
formatList(combined.statuses.filter((status) => status.state !== 'success')
|
|
||||||
.map((status) => `${status.context} (${status.state})`))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Requires the check to have actually run, not merely to have not failed.
|
|
||||||
const requiredCheck = checkRuns.find((run) =>
|
|
||||||
run.name === REQUIRED_CHECK &&
|
|
||||||
run.app && run.app.slug === 'github-actions' &&
|
|
||||||
run.status === 'completed' && OK_CONCLUSIONS.has(run.conclusion));
|
|
||||||
|
|
||||||
if (!requiredCheck) {
|
|
||||||
return refuse(
|
|
||||||
`the \`${REQUIRED_CHECK}\` check has not succeeded on \`${headSha.slice(0, 7)}\`. ` +
|
|
||||||
'If it never ran, a maintainer needs to approve the workflow run first.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const scopeSummary = `${files.length} file(s), all within:\n${formatList(grants)}`;
|
|
||||||
|
|
||||||
if (dryRun) {
|
|
||||||
core.info('Dry run: every gate passed, not merging.');
|
|
||||||
await react('+1');
|
|
||||||
await say(
|
|
||||||
`@${commenter} **dry run** - this PR passes every gate and I *would* squash-merge it ` +
|
|
||||||
`at \`${headSha.slice(0, 7)}\`.\n\nVerified scope: ${scopeSummary}`
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- re-validate, then merge ----
|
|
||||||
// An unchanged head SHA means the verified file list still holds.
|
|
||||||
const { data: fresh } = await github.rest.pulls.get({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
pull_number: pr.number
|
|
||||||
});
|
|
||||||
|
|
||||||
if (fresh.head.sha !== headSha || fresh.base.ref !== pr.base.ref || fresh.state !== 'open' || fresh.merged || fresh.draft) {
|
|
||||||
return refuse('it changed while I was checking it. Nothing was merged - re-run `/bot merge`.');
|
|
||||||
}
|
|
||||||
|
|
||||||
let merged;
|
|
||||||
try {
|
|
||||||
// Pinned to the verified head: a moved head fails with 409.
|
|
||||||
({ data: merged } = await github.rest.pulls.merge({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
pull_number: pr.number,
|
|
||||||
sha: headSha,
|
|
||||||
merge_method: MERGE_METHOD,
|
|
||||||
commit_title: `${pr.title} (#${pr.number})`,
|
|
||||||
commit_message:
|
|
||||||
`Merged by /bot merge on behalf of @${commenter} (id ${comment.user.id}).\n` +
|
|
||||||
`Grants: ${grants.join(', ')}\nHead: ${headSha}\n`
|
|
||||||
}));
|
|
||||||
} catch (error) {
|
|
||||||
const hint = {
|
|
||||||
403: 'the workflow token cannot write to the repository.',
|
|
||||||
405: 'GitHub refused the merge - branch protection, a required review or check, a newly added CODEOWNERS file, or squash merging being disabled.',
|
|
||||||
409: `the head commit moved after I verified it (was \`${headSha.slice(0, 7)}\`).`,
|
|
||||||
422: 'GitHub rejected the merge as invalid.'
|
|
||||||
}[error.status];
|
|
||||||
|
|
||||||
if (!hint) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
await refuse(`${hint}\n\n> ${error.message}\n\nNothing was merged.`);
|
|
||||||
core.setFailed(`Delegated merge failed: ${error.status} ${error.message}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
core.info(`Merged #${pr.number} as ${merged.sha}.`);
|
|
||||||
await react('rocket');
|
|
||||||
await say(
|
|
||||||
`@${commenter} squash-merged into \`${pr.base.ref}\` as ${merged.sha}.\n\nVerified scope: ${scopeSummary}`
|
|
||||||
);
|
|
||||||
|
|
||||||
// ---- re-kick the build ----
|
|
||||||
// A GITHUB_TOKEN merge fires no push event, so build_all.yml would
|
|
||||||
// otherwise never see these files.
|
|
||||||
try {
|
|
||||||
await github.rest.actions.createWorkflowDispatch({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
workflow_id: 'build_all.yml',
|
|
||||||
ref: pr.base.ref
|
|
||||||
});
|
|
||||||
core.info(`Dispatched build_all.yml on ${pr.base.ref}.`);
|
|
||||||
} catch (error) {
|
|
||||||
core.warning(`Merged successfully, but dispatching build_all.yml failed: ${error.message}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
label-profile:
|
|
||||||
# Independent of the merge rules: any PR that changes only files inside
|
|
||||||
# resources/profiles/ is labeled `profile`.
|
|
||||||
if: >-
|
|
||||||
github.repository == 'OrcaSlicer/OrcaSlicer'
|
|
||||||
&& github.event_name == 'pull_request_target'
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: read
|
|
||||||
issues: write
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 5
|
|
||||||
steps:
|
|
||||||
- name: Label profile-only PRs
|
|
||||||
uses: actions/github-script@v9
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
function isPermissionDenied(error) {
|
|
||||||
return error && error.status === 403 && /Resource not accessible by integration/i.test(error.message || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
const PROFILE_ROOT = 'resources/profiles/';
|
|
||||||
const LABEL = 'profile';
|
|
||||||
const LISTFILES_CAP = 3000;
|
|
||||||
const ATTEMPTS = 3;
|
|
||||||
|
|
||||||
function profileOnlyProblem(pr, files) {
|
|
||||||
if (!files.length) {
|
|
||||||
return 'PR changes no files; not labeling.';
|
|
||||||
}
|
|
||||||
// A truncated list, or a count that disagrees with the PR, cannot
|
|
||||||
// prove "only profile files".
|
|
||||||
if (files.length >= LISTFILES_CAP || files.length !== pr.changed_files) {
|
|
||||||
return `PR reports ${pr.changed_files} changed files but the API listed ${files.length}; not labeling.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both endpoints of a rename count, so a move out of the profile
|
|
||||||
// root is not mistaken for a profile-only change.
|
|
||||||
const paths = files.flatMap((file) => [file.filename, file.previous_filename].filter(Boolean));
|
|
||||||
const outside = paths.filter((path) => !path.startsWith(PROFILE_ROOT));
|
|
||||||
|
|
||||||
if (outside.length) {
|
|
||||||
return `${outside.length} changed path(s) fall outside ${PROFILE_ROOT}; not labeling.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { owner, repo } = context.repo;
|
|
||||||
const number = context.payload.pull_request.number;
|
|
||||||
|
|
||||||
// The event payload is frozen at `opened`; listFiles is not. Read
|
|
||||||
// fresh PR metadata and retry if either side of the diff changes.
|
|
||||||
for (let attempt = 0; attempt < ATTEMPTS; attempt += 1) {
|
|
||||||
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: number });
|
|
||||||
|
|
||||||
if (pr.state !== 'open') {
|
|
||||||
core.info(`PR is ${pr.state}; not labeling.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
pull_number: pr.number,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
const problem = profileOnlyProblem(pr, files);
|
|
||||||
|
|
||||||
const { data: after } = await github.rest.pulls.get({ owner, repo, pull_number: number });
|
|
||||||
if (
|
|
||||||
after.state !== 'open' ||
|
|
||||||
after.head.sha !== pr.head.sha ||
|
|
||||||
after.base.ref !== pr.base.ref ||
|
|
||||||
after.base.sha !== pr.base.sha
|
|
||||||
) {
|
|
||||||
core.info('PR changed while listing files; retrying.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (problem) {
|
|
||||||
core.info(problem);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
labels: [LABEL]
|
|
||||||
});
|
|
||||||
core.info(`Applied the "${LABEL}" label.`);
|
|
||||||
} catch (error) {
|
|
||||||
if (isPermissionDenied(error)) {
|
|
||||||
core.warning(`Cannot add the "${LABEL}" label because the token cannot write.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
core.warning('PR kept changing during verification; not labeling.');
|
|
||||||
|
|
||||||
label-profile-partner:
|
|
||||||
# Labels a profile PR whose author holds a grant covering every changed
|
|
||||||
# path, and explains the /bot merge command to them once.
|
|
||||||
if: >-
|
|
||||||
github.repository == 'OrcaSlicer/OrcaSlicer'
|
|
||||||
&& github.event_name == 'pull_request_target'
|
|
||||||
permissions:
|
|
||||||
contents: read # delegatable subtree, for file modes
|
|
||||||
pull-requests: read
|
|
||||||
issues: write # label + comment
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
timeout-minutes: 10
|
|
||||||
# Supplies FOLDER_MERGERS. Must carry no protection rules, or every
|
|
||||||
# qualifying PR open would wait for a human reviewer.
|
|
||||||
environment: merge-delegation
|
|
||||||
steps:
|
|
||||||
- name: Label profile PRs from delegated maintainers
|
|
||||||
uses: actions/github-script@v9
|
|
||||||
env:
|
|
||||||
# Read as an env var, never interpolated into the script body.
|
|
||||||
FOLDER_MERGERS: ${{ vars.FOLDER_MERGERS }}
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
function isPermissionDenied(error) {
|
|
||||||
return error && error.status === 403 && /Resource not accessible by integration/i.test(error.message || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Never prints the grant list: this job posts public comments and
|
|
||||||
// its logs are public too.
|
|
||||||
async function bestEffort(call, warning) {
|
|
||||||
try {
|
|
||||||
await call();
|
|
||||||
} catch (error) {
|
|
||||||
if (isPermissionDenied(error)) {
|
|
||||||
core.warning(warning);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const MARKER = '<!-- profile-partner-bot -->';
|
|
||||||
const LABEL = 'orca profile partner';
|
|
||||||
const ATTEMPTS = 3;
|
|
||||||
|
|
||||||
// ---- scope rules, mirrored from the merge job above ----
|
|
||||||
// Change both together: these decide whether a delegate could merge.
|
|
||||||
const DELEGATABLE_ROOT = 'resources/profiles/';
|
|
||||||
const ALLOWED_BASE_BRANCH = /^(?:main|release\/.+)$/;
|
|
||||||
const LISTFILES_CAP = 3000;
|
|
||||||
const REGULAR_FILE_MODES = new Set(['100644', '100755']);
|
|
||||||
|
|
||||||
const DENIED_PATTERNS = [
|
|
||||||
/^\.github\//,
|
|
||||||
/(^|\/)\.git(attributes|modules|ignore|config)$/,
|
|
||||||
/^(?:src|deps|deps_src|tests|tools|cmake|sandboxes|scripts|docs?|localization|bbl)\//,
|
|
||||||
/(^|\/)cmakelists\.txt$/,
|
|
||||||
/\.cmake$/,
|
|
||||||
/^build_[^/]*\.(?:sh|bat)$/,
|
|
||||||
/^version\.inc$/,
|
|
||||||
// Executables, including those inside the delegatable root.
|
|
||||||
/\.(?:sh|bash|bat|cmd|ps1|py|js|mjs|cjs|ts|rb|pl|php)$/
|
|
||||||
];
|
|
||||||
|
|
||||||
function parseGrants(raw) {
|
|
||||||
// GitHub login: 1-39 chars, alphanumerics with single interior hyphens.
|
|
||||||
const loginPattern = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?=[A-Za-z0-9])){0,38}$/;
|
|
||||||
const grantsByLogin = new Map();
|
|
||||||
const problems = [];
|
|
||||||
|
|
||||||
(raw || '').split(/\r?\n/).forEach((rawLine, index) => {
|
|
||||||
const line = rawLine.trim();
|
|
||||||
if (!line || line.startsWith('#')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Splits on the first colon only, so paths may contain ':' and spaces.
|
|
||||||
const separator = line.indexOf(':');
|
|
||||||
if (separator === -1) {
|
|
||||||
problems.push(`line ${index + 1}: expected \`account: path\``);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const login = line.slice(0, separator).trim().replace(/^@/, '');
|
|
||||||
const path = line.slice(separator + 1).trim().replace(/\/+$/, '');
|
|
||||||
|
|
||||||
if (!loginPattern.test(login)) {
|
|
||||||
problems.push(`line ${index + 1}: \`${login}\` is not a valid GitHub account name`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (/[\\*?\u0000-\u001f\u007f]/.test(path) || path.split('/').includes('..') || path.includes('//')) {
|
|
||||||
problems.push(`line ${index + 1}: invalid path (no globs, \`..\`, \`//\`, backslashes or control characters)`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Rejects anything outside the root, and the bare root itself.
|
|
||||||
if (!path.startsWith(DELEGATABLE_ROOT) || path.length <= DELEGATABLE_ROOT.length) {
|
|
||||||
problems.push(`line ${index + 1}: \`${path}\` is not inside \`${DELEGATABLE_ROOT}\``);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const key = login.toLowerCase();
|
|
||||||
grantsByLogin.set(key, (grantsByLogin.get(key) || []).concat(path));
|
|
||||||
});
|
|
||||||
|
|
||||||
return { grantsByLogin, problems };
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDenied(path) {
|
|
||||||
if (/[\\\u0000-\u001f\u007f]/.test(path) || path.startsWith('/') || path.split('/').includes('..')) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalized = path.normalize('NFKC').toLowerCase();
|
|
||||||
return DENIED_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Byte-exact match on directory boundaries, so a grant of
|
|
||||||
// `.../Acme` covers neither `.../Acme Labs/x.json` nor `.../Acme.json`.
|
|
||||||
function isGranted(path, grants) {
|
|
||||||
return grants.some((grant) => path === grant || path.startsWith(`${grant}/`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Both endpoints of a rename; both must satisfy the grant.
|
|
||||||
function pathsFor(file) {
|
|
||||||
return [file.filename, file.previous_filename].filter(Boolean);
|
|
||||||
}
|
|
||||||
// ---- end mirrored rules ----
|
|
||||||
|
|
||||||
function scopeProblem(pr, files, grants) {
|
|
||||||
if (!files.length) {
|
|
||||||
return 'PR changes no files; not labeling.';
|
|
||||||
}
|
|
||||||
if (files.length >= LISTFILES_CAP || files.length !== pr.changed_files) {
|
|
||||||
return `PR reports ${pr.changed_files} changed files but the API listed ${files.length}; not labeling.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
let outsideCount = 0;
|
|
||||||
for (const file of files) {
|
|
||||||
for (const path of pathsFor(file)) {
|
|
||||||
if (isDenied(path) || !isGranted(path, grants)) {
|
|
||||||
outsideCount += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outsideCount) {
|
|
||||||
return `PR has ${outsideCount} path(s) outside @${author}'s grants; not labeling.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- file modes: rejects symlinks and submodules ----
|
|
||||||
function modeProblem(files, tree) {
|
|
||||||
if (tree.truncated) {
|
|
||||||
return 'The profile tree is too large to verify file modes; not labeling.';
|
|
||||||
}
|
|
||||||
|
|
||||||
const modesByPath = new Map(tree.tree.map((entry) => [`${DELEGATABLE_ROOT}${entry.path}`, entry.mode]));
|
|
||||||
const hasIrregularFile = files.some((file) =>
|
|
||||||
file.status !== 'removed' && !REGULAR_FILE_MODES.has(modesByPath.get(file.filename)));
|
|
||||||
|
|
||||||
if (hasIrregularFile) {
|
|
||||||
return 'PR adds symlinks, submodules or files whose modes cannot be verified; not labeling.';
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { owner, repo } = context.repo;
|
|
||||||
const number = context.payload.pull_request.number;
|
|
||||||
const author = context.payload.pull_request.user.login;
|
|
||||||
|
|
||||||
const { grantsByLogin, problems } = parseGrants(process.env.FOLDER_MERGERS);
|
|
||||||
|
|
||||||
// Only the count: the malformed lines may name grant holders.
|
|
||||||
if (problems.length) {
|
|
||||||
core.warning(`FOLDER_MERGERS has ${problems.length} malformed line(s); not labeling.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const grants = grantsByLogin.get(author.toLowerCase()) || [];
|
|
||||||
// Says nothing to accounts with no grant, so it cannot be used to spam.
|
|
||||||
if (!grants.length) {
|
|
||||||
core.info(`Ignoring PR from @${author}: not listed in FOLDER_MERGERS.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read current PR metadata for the file list and head tree. Retry
|
|
||||||
// if either side of the diff changes during verification.
|
|
||||||
for (let attempt = 0; attempt < ATTEMPTS; attempt += 1) {
|
|
||||||
const { data: pr } = await github.rest.pulls.get({ owner, repo, pull_number: number });
|
|
||||||
|
|
||||||
if (pr.state !== 'open') {
|
|
||||||
core.info(`PR is ${pr.state}; not labeling.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!ALLOWED_BASE_BRANCH.test(pr.base.ref)) {
|
|
||||||
core.info(`PR targets "${pr.base.ref}", not main or release/*; not labeling.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Checked before listing files, so a PR too large to list is
|
|
||||||
// rejected in one call.
|
|
||||||
if (pr.changed_files >= LISTFILES_CAP) {
|
|
||||||
core.info(`PR changes ${pr.changed_files} files, more than the API can list; not labeling.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const files = await github.paginate(github.rest.pulls.listFiles, {
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
pull_number: pr.number,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
const scopeIssue = scopeProblem(pr, files, grants);
|
|
||||||
|
|
||||||
let modeIssue = null;
|
|
||||||
if (!scopeIssue) {
|
|
||||||
const { data: tree } = await github.rest.git.getTree({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
tree_sha: `${pr.head.sha}:${DELEGATABLE_ROOT.replace(/\/$/, '')}`,
|
|
||||||
recursive: 'true'
|
|
||||||
});
|
|
||||||
modeIssue = modeProblem(files, tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { data: after } = await github.rest.pulls.get({ owner, repo, pull_number: number });
|
|
||||||
if (
|
|
||||||
after.state !== 'open' ||
|
|
||||||
after.head.sha !== pr.head.sha ||
|
|
||||||
after.base.ref !== pr.base.ref ||
|
|
||||||
after.base.sha !== pr.base.sha
|
|
||||||
) {
|
|
||||||
core.info('PR changed while verifying; retrying.');
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const problem = scopeIssue || modeIssue;
|
|
||||||
if (problem) {
|
|
||||||
core.info(problem);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---- label + one-time comment ----
|
|
||||||
await bestEffort(
|
|
||||||
() => github.rest.issues.addLabels({ owner, repo, issue_number: pr.number, labels: [LABEL] }),
|
|
||||||
`Cannot add the "${LABEL}" label because the token cannot write.`);
|
|
||||||
|
|
||||||
const comments = await github.paginate(github.rest.issues.listComments, {
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
if (comments.some((comment) => (comment.body || '').includes(MARKER))) {
|
|
||||||
core.info('Partner notice already present; skipping comment.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await bestEffort(
|
|
||||||
() => github.rest.issues.createComment({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number: pr.number,
|
|
||||||
body:
|
|
||||||
`${MARKER}\n` +
|
|
||||||
`Hi @${author}, this profile PR is covered by your delegated merge grant.\n\n` +
|
|
||||||
`Once it is ready for review and CI is green, you can merge it yourself:\n\n` +
|
|
||||||
`- \`/bot merge\` - squash-merge into \`main\` or \`release/*\`\n` +
|
|
||||||
`- \`/bot merge --dry-run\` - report the verdict without merging\n\n` +
|
|
||||||
`The bot re-checks the scope, the file modes and the \`Check profiles\` check at merge time.`
|
|
||||||
}),
|
|
||||||
'Cannot post the partner notice because the token cannot write comments.');
|
|
||||||
|
|
||||||
core.info(`Applied the "${LABEL}" label and posted the /bot merge notice.`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
core.warning('PR kept changing during verification; not labeling.');
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
name: Publish to draft release
|
|
||||||
|
|
||||||
# Manually pulls the platform binaries produced by a "Build all" run and uploads
|
|
||||||
# them to an existing DRAFT release. Decoupled from the build so you can test a
|
|
||||||
# build first, then publish exactly that run's artifacts once you're happy.
|
|
||||||
#
|
|
||||||
# Trigger: Actions tab -> "Publish to draft release" -> Run workflow.
|
|
||||||
# Locked to a single person: the first step aborts unless the actor matches the
|
|
||||||
# `RELEASE_PUBLISHER` repo variable (set to "SoftFever"). To allow someone else,
|
|
||||||
# change that variable: gh variable set RELEASE_PUBLISHER --body "<login>".
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
run_id:
|
|
||||||
description: 'Run ID of the "Build all" workflow to pull binaries from (the number in the Actions run URL)'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
tag:
|
|
||||||
description: 'Tag of the draft release to upload to (e.g. v2.4.0-beta)'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write # upload release assets
|
|
||||||
actions: read # download artifacts from another run
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
name: Publish binaries to draft release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
RUN_ID: ${{ inputs.run_id }}
|
|
||||||
TAG: ${{ inputs.tag }}
|
|
||||||
steps:
|
|
||||||
- name: Restrict to the release publisher
|
|
||||||
env:
|
|
||||||
PUBLISHER: ${{ vars.RELEASE_PUBLISHER }}
|
|
||||||
run: |
|
|
||||||
if [ -z "$PUBLISHER" ]; then
|
|
||||||
echo "::error::RELEASE_PUBLISHER repo variable is not set."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if [ "${{ github.actor }}" != "$PUBLISHER" ]; then
|
|
||||||
echo "::error::Only @$PUBLISHER may run this workflow (you are @${{ github.actor }})."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo "Authorized: @${{ github.actor }}"
|
|
||||||
|
|
||||||
- name: Verify target is a draft release
|
|
||||||
run: |
|
|
||||||
if ! gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json isDraft >/dev/null 2>&1; then
|
|
||||||
echo "::error::Release '$TAG' not found. Create the draft (with this tag) first."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
is_draft=$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json isDraft --jq '.isDraft')
|
|
||||||
if [ "$is_draft" != "true" ]; then
|
|
||||||
echo "::error::Release '$TAG' is published, not a draft. Aborting to avoid touching a live release."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Verify source build run
|
|
||||||
run: |
|
|
||||||
info=$(gh run view "$RUN_ID" --repo "$GITHUB_REPOSITORY" --json workflowName,conclusion,headBranch)
|
|
||||||
name=$(echo "$info" | jq -r '.workflowName')
|
|
||||||
conclusion=$(echo "$info" | jq -r '.conclusion')
|
|
||||||
branch=$(echo "$info" | jq -r '.headBranch')
|
|
||||||
echo "Source run #$RUN_ID: '$name' on '$branch' (conclusion: $conclusion)"
|
|
||||||
if [ "$conclusion" != "success" ]; then
|
|
||||||
echo "::warning::Source run did not conclude 'success' ($conclusion). Some assets may be missing."
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Download release artifacts from build run
|
|
||||||
run: |
|
|
||||||
# Windows_V* (not Windows_*) keeps the MSIX Store artifact out: it goes to Partner Center, not GitHub releases.
|
|
||||||
gh run download "$RUN_ID" --repo "$GITHUB_REPOSITORY" --dir artifacts \
|
|
||||||
-p 'OrcaSlicer_Windows_V*' \
|
|
||||||
-p 'OrcaSlicer_Mac_universal_*' \
|
|
||||||
-p 'OrcaSlicer_Linux_ubuntu_*' \
|
|
||||||
-p 'OrcaSlicer-Linux-flatpak_*' \
|
|
||||||
-p 'PDB'
|
|
||||||
echo "Downloaded artifact folders:"
|
|
||||||
ls -1 artifacts
|
|
||||||
|
|
||||||
- name: Assemble release assets
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
mkdir -p upload
|
|
||||||
|
|
||||||
# gh run download auto-extracts each artifact into a folder, so the inner
|
|
||||||
# binaries are already unzipped. Copy the inner binary for each platform.
|
|
||||||
# -type f is required (some artifact *folders* are named "*.flatpak").
|
|
||||||
|
|
||||||
# Windows installers (x64 + arm64): the .exe inside each installer
|
|
||||||
# artifact, NOT the orca-slicer.exe in the portable app folder. CPack
|
|
||||||
# now bakes the arch into the filename (…_x64.exe / …_arm64.exe), so
|
|
||||||
# copy them straight through.
|
|
||||||
find artifacts -type f -name '*.exe' -path '*OrcaSlicer_Windows_V*' ! -path '*_portable*' -exec cp -v {} upload/ \;
|
|
||||||
# macOS universal DMG (profile-validator DMG isn't downloaded).
|
|
||||||
find artifacts -type f -name '*.dmg' -path '*OrcaSlicer_Mac_universal_*' -exec cp -v {} upload/ \;
|
|
||||||
# Linux AppImage.
|
|
||||||
find artifacts -type f -name '*.AppImage' -exec cp -v {} upload/ \;
|
|
||||||
# Flatpak bundles (x86_64 + aarch64).
|
|
||||||
find artifacts -type f -name '*.flatpak' -exec cp -v {} upload/ \;
|
|
||||||
# Windows debug symbols (PDB archive, for developers).
|
|
||||||
find artifacts -type f -name 'Debug_PDB_*.7z' -exec cp -v {} upload/ \;
|
|
||||||
|
|
||||||
# Portable Windows builds (x64 + arm64) are unzipped folder artifacts;
|
|
||||||
# re-zip each to its released filename (these stay .zip on the release).
|
|
||||||
mapfile -t portable_dirs < <(find artifacts -maxdepth 1 -type d -name 'OrcaSlicer_Windows_*_portable')
|
|
||||||
if [ ${#portable_dirs[@]} -eq 0 ]; then
|
|
||||||
echo "::warning::Windows portable artifact not found."
|
|
||||||
fi
|
|
||||||
for portable_dir in "${portable_dirs[@]}"; do
|
|
||||||
( cd "$portable_dir" && zip -qr "$GITHUB_WORKSPACE/upload/$(basename "$portable_dir").zip" . )
|
|
||||||
echo "Zipped portable -> $(basename "$portable_dir").zip"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Assets to upload:"
|
|
||||||
ls -lh upload
|
|
||||||
if [ -z "$(ls -A upload)" ]; then
|
|
||||||
echo "::error::No assets assembled. Check the run_id and that its artifacts haven't expired."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload assets to draft release
|
|
||||||
run: |
|
|
||||||
gh release upload "$TAG" upload/* --repo "$GITHUB_REPOSITORY" --clobber
|
|
||||||
echo "Uploaded to draft release: $TAG"
|
|
||||||
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name'
|
|
||||||
@@ -21,7 +21,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Cache shellcheck download
|
- name: Cache shellcheck download
|
||||||
id: cache-shellcheck-v0_11
|
id: cache-shellcheck-v0_11
|
||||||
uses: actions/cache@v6
|
uses: actions/cache@v5
|
||||||
with:
|
with:
|
||||||
path: ~/shellcheck
|
path: ~/shellcheck
|
||||||
key: ${{ runner.os }}-shellcheck-v0_11
|
key: ${{ runner.os }}-shellcheck-v0_11
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
tar -xvf ~/sc.tar.xz -C ~
|
tar -xvf ~/sc.tar.xz -C ~
|
||||||
mv ~/shellcheck-"${INPUT_VERSION}"/shellcheck ~/shellcheck
|
mv ~/shellcheck-"${INPUT_VERSION}"/shellcheck ~/shellcheck
|
||||||
|
|
||||||
- uses: actions/checkout@v7
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
name: Unit Tests
|
|
||||||
|
|
||||||
# Download a platform's test artifact, run ctest, and upload the JUnit
|
|
||||||
# results for aggregation. Called once per arch from build_all.yml.
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
os:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
artifact:
|
|
||||||
description: Test artifact uploaded by the build leg
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
test-dir:
|
|
||||||
description: Built tests dir; defaults to build/tests, override for arch-separated builds
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
default: build/tests
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
unit_tests:
|
|
||||||
# Static "Unit Tests"; the per-arch label is the caller's job name, so the
|
|
||||||
# graph shows e.g. "Windows x64 / Unit Tests".
|
|
||||||
name: Unit Tests
|
|
||||||
runs-on: ${{ inputs.os }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v7
|
|
||||||
with:
|
|
||||||
# Tests reach outside tests/ at runtime: tests/data (TEST_DATA_DIR) and
|
|
||||||
# resources/profiles (PROFILES_DIR) by baked-in absolute path, plus
|
|
||||||
# resources/info (nozzle data) via resources_dir() during a real slice.
|
|
||||||
# Check out all of resources/ so no test hits a missing-file path.
|
|
||||||
sparse-checkout: |
|
|
||||||
.github
|
|
||||||
scripts
|
|
||||||
tests
|
|
||||||
resources
|
|
||||||
- name: Apt-Install Dependencies
|
|
||||||
if: runner.os == 'Linux' && !vars.SELF_HOSTED
|
|
||||||
uses: ./.github/actions/apt-install-deps
|
|
||||||
- name: Restore Test Artifact
|
|
||||||
uses: actions/download-artifact@v8
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.artifact }}
|
|
||||||
- uses: lukka/get-cmake@latest
|
|
||||||
with:
|
|
||||||
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
|
|
||||||
useLocalCache: true
|
|
||||||
useCloudCache: true
|
|
||||||
- name: Unpackage and Run Unit Tests
|
|
||||||
timeout-minutes: 20
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
tar -xvf build_tests.tar
|
|
||||||
# Every platform builds with a multi-config generator (build_linux.sh uses Ninja
|
|
||||||
# Multi-Config), so ctest needs the config: without it, plain add_test() tests
|
|
||||||
# lose their labels and report "Not Run".
|
|
||||||
scripts/run_unit_tests.sh "${{ inputs.test-dir }}" Release
|
|
||||||
- name: Upload Test Logs
|
|
||||||
if: ${{ failure() }}
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: unit-test-logs-${{ inputs.artifact }}
|
|
||||||
path: ${{ inputs.test-dir }}/**/*.log
|
|
||||||
- name: Upload Test Results
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: test-results-${{ inputs.artifact }}
|
|
||||||
path: ctest_results.xml
|
|
||||||
retention-days: 5
|
|
||||||
if-no-files-found: warn
|
|
||||||
- name: Delete Test Artifact
|
|
||||||
if: success()
|
|
||||||
uses: geekyeggo/delete-artifact@v6
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.artifact }}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
name: Flatpak Unit Tests
|
|
||||||
|
|
||||||
# Run the flatpak build's test asset inside the sandbox, once per arch. The
|
|
||||||
# GNOME SDK's _GLIBCXX_ASSERTIONS gives a bounds-checked STL that catches
|
|
||||||
# out-of-bounds reads no other test leg does.
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
os:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
artifact:
|
|
||||||
description: Test asset uploaded by the flatpak build leg
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
unit_tests_flatpak:
|
|
||||||
name: Flatpak Unit Tests
|
|
||||||
runs-on: ${{ inputs.os }}
|
|
||||||
container:
|
|
||||||
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-50
|
|
||||||
options: --privileged
|
|
||||||
steps:
|
|
||||||
- name: Restore test asset
|
|
||||||
uses: actions/download-artifact@v8
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.artifact }}
|
|
||||||
- name: Run unit tests (bounds-checked sandbox)
|
|
||||||
timeout-minutes: 20
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
tar -xf flatpak-test-asset.tar
|
|
||||||
# Recreate the stable module symlink so /run/build/OrcaSlicer resolves.
|
|
||||||
d=$(ls -d .flatpak-builder/build/OrcaSlicer-* | tail -1)
|
|
||||||
ln -sfn "$(basename "$d")" .flatpak-builder/build/OrcaSlicer
|
|
||||||
# The runtime + SDK + the llvm extension the app metadata references,
|
|
||||||
# which `flatpak build` mounts; best-effort, the image may have them.
|
|
||||||
flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo
|
|
||||||
flatpak install --user -y --noninteractive flathub \
|
|
||||||
org.gnome.Platform//50 org.gnome.Sdk//50 org.freedesktop.Sdk.Extension.llvm21//25.08 || true
|
|
||||||
# `flatpak build` uses bwrap (no rofiles-fuse, which this container
|
|
||||||
# rejects); bind-mount the build tree so the baked TEST_DATA_DIR resolves.
|
|
||||||
flatpak build --die-with-parent --share=network \
|
|
||||||
--bind-mount=/run/build="$PWD/.flatpak-builder/build" \
|
|
||||||
flatpak_app \
|
|
||||||
bash -c 'cd /run/build/OrcaSlicer && scripts/run_unit_tests.sh build_flatpak/tests'
|
|
||||||
- name: Collect test results
|
|
||||||
if: always()
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
d=$(ls -d .flatpak-builder/build/OrcaSlicer-* 2>/dev/null | tail -1 || true)
|
|
||||||
[ -n "$d" ] && [ -f "$d/ctest_results.xml" ] && cp "$d/ctest_results.xml" ctest_results.xml || true
|
|
||||||
- name: Upload Test Results
|
|
||||||
if: always()
|
|
||||||
uses: actions/upload-artifact@v7
|
|
||||||
with:
|
|
||||||
name: test-results-${{ inputs.artifact }}
|
|
||||||
path: ctest_results.xml
|
|
||||||
retention-days: 5
|
|
||||||
if-no-files-found: warn
|
|
||||||
- name: Delete Test Asset
|
|
||||||
if: success()
|
|
||||||
uses: geekyeggo/delete-artifact@v6
|
|
||||||
with:
|
|
||||||
name: ${{ inputs.artifact }}
|
|
||||||
failOnError: false
|
|
||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v7
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
Build
|
Build
|
||||||
Build.bat
|
Build.bat
|
||||||
/build*/
|
/build*/
|
||||||
/out/
|
|
||||||
CMakeLists.txt.user
|
CMakeLists.txt.user
|
||||||
CMakeUserPresets.json
|
|
||||||
**/CMakeLists.txt.autosave
|
**/CMakeLists.txt.autosave
|
||||||
deps/build*
|
deps/build*
|
||||||
MYMETA.json
|
MYMETA.json
|
||||||
@@ -48,9 +46,3 @@ test.js
|
|||||||
internal_docs/
|
internal_docs/
|
||||||
*.flatpak
|
*.flatpak
|
||||||
/flatpak-repo/
|
/flatpak-repo/
|
||||||
# Python bytecode
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
*.opc
|
|
||||||
/.test/
|
|
||||||
docs/superpowers/
|
|
||||||
@@ -17,27 +17,19 @@ cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
|||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Catch2 framework. Tests in `tests/`; see [tests/AGENTS.md](tests/AGENTS.md) for where a new test belongs and the conventions to follow.
|
Catch2 framework. Tests in `tests/` directory.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd build && ctest -C Release --output-on-failure # all tests
|
cd build && ctest --output-on-failure # all tests
|
||||||
ctest --test-dir ./tests/libslic3r -C Release # individual suite
|
ctest --test-dir ./tests/libslic3r # individual suite
|
||||||
ctest --test-dir ./tests/fff_print -C Release
|
ctest --test-dir ./tests/fff_print
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
|
||||||
|
|
||||||
- Docs live in `docs/`; the high-level design of a subsystem goes in `docs/HLSD/<subsystem>.md`.
|
|
||||||
- Describe the design as it stands — what the subsystem does, why it exists, and the constraints that shape it. Not the route that got there: no phases, task lists, status markers, or "before/after this PR" framing.
|
|
||||||
- Planning and investigation output (brainstorms, superpowers design and plan docs) stays in `docs/superpowers/`, which is gitignored. Never commit it.
|
|
||||||
- Write a doc only when the design is not evident from the code, and when a change invalidates an existing one, update it in the same PR.
|
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
- C++17, selective C++20. PascalCase classes, snake_case functions/variables
|
- C++17, selective C++20. PascalCase classes, snake_case functions/variables
|
||||||
- `#pragma once` for headers. Smart pointers and RAII preferred
|
- `#pragma once` for headers. Smart pointers and RAII preferred
|
||||||
- Parallelization via TBB — be mindful of shared state
|
- Parallelization via TBB — be mindful of shared state
|
||||||
- Always use `SetSizerAndFit(sizer)` instead of `SetSizer(sizer)` on top level window. Unless `SetSizer` must be called before the full layout is built, call `sizer->SetSizeHints(window)` afterwards in this case.
|
|
||||||
|
|
||||||
## Key Entry Points
|
## Key Entry Points
|
||||||
|
|
||||||
@@ -63,36 +55,3 @@ ctest --test-dir ./tests/fff_print -C Release
|
|||||||
- Add helper functions or utilities only when existing code cannot reasonably be reused. Avoid duplication.
|
- Add helper functions or utilities only when existing code cannot reasonably be reused. Avoid duplication.
|
||||||
- Keep code concise and clear. Manually simplify AI generated bloated codes before review.
|
- Keep code concise and clear. Manually simplify AI generated bloated codes before review.
|
||||||
- Include targeted tests or documented verification for behavior changes, especially in slicing logic, profiles, formats, and GUI defaults.
|
- Include targeted tests or documented verification for behavior changes, especially in slicing logic, profiles, formats, and GUI defaults.
|
||||||
- For profile changes (`resources/profiles/<Vendor>/**`), check that `version` in the sibling `resources/profiles/<Vendor>.json` was bumped.
|
|
||||||
- For translation changes (`localization/i18n/**/*.po`), check that recurring terms match the [Localization glossary](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/main/developer_reference/localization_glossary.md) for that language.
|
|
||||||
|
|
||||||
## Localization & translations
|
|
||||||
|
|
||||||
Catalogs live in `localization/i18n/<lang>/OrcaSlicer_<lang>.po`; the template is `OrcaSlicer.pot`.
|
|
||||||
See the [Localization guide](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/main/developer_reference/localization_guide.md) for the human-facing version of these principles.
|
|
||||||
|
|
||||||
### Terminology
|
|
||||||
|
|
||||||
- Use the [Localization glossary](https://github.com/OrcaSlicer/OrcaSlicer_WIKI/blob/main/developer_reference/localization_glossary.md) as the source of truth for recurring terms, so the same English term is always rendered the same way within a language, and terms that must stay in English (brand/product names, acronyms, materials, file formats, G-code tokens, macros/variables/identifiers) are not translated.
|
|
||||||
- If a term's established translation changes, update both the affected `.po` files and the glossary (`localization_glossary.tsv`, then regenerate) so they stay in sync.
|
|
||||||
- Translate the *meaning*, not the words. Check what the string actually controls before translating it — English reuses one word for different things. `Flow ratio` (multiplier), `Flow Rate` (throughput) and `Flow Dynamics` (pressure compensation) are three different terms; `extruder` may mean the toolhead, the feeder motor, or the nozzle depending on the string.
|
|
||||||
- Reuse one template per recurring message shape (`Failed to connect to …`, `Are you sure you want to …?`), even where the English wording varies.
|
|
||||||
|
|
||||||
### Editing rules
|
|
||||||
|
|
||||||
- Only edit `msgstr` — **never** change `msgid`, and never "fix" wrong English in the translation alone. Report the source string instead.
|
|
||||||
- Preserve exactly: placeholders (`%s`, `%d`, `%1%`, `%zu`, `%%`), every `\n` (count *and* position, including leading/trailing), leading/trailing spaces, HTML tags, `℃`, and the file's encoding and line endings.
|
|
||||||
- **Never reorder positional arguments** in a `c-format` string. If the msgid is `%d` then `%s`, that order must hold — swapping them breaks at runtime.
|
|
||||||
- `msgctxt` separates homonyms — always read it. `Back`/`Camera View` is the rear view of the 3D navigator, while `Back`/`Navigation` is the go-back button; `Top` exists in the *Alignment*, *Layers* and *Camera View* senses.
|
|
||||||
- When a string needs disambiguating, add context in the source (`_L_CONTEXT`/`_u8L_CONTEXT`), don't work around it in the translation.
|
|
||||||
- A literal `%` inside a string xgettext flagged `possible-c-format` will fail `msgfmt`. Fix it with a `// xgettext:no-c-format, no-boost-format` comment above the string in the source — do not mangle the translation or use `%%` in text that is never passed through printf.
|
|
||||||
- Plural entries: read `nplurals` from the catalog's `Plural-Forms` header (it is **not** always 2 — ja/ko/zh/th/vi use 1, ru/cs/pl/lt use 3, uk uses 4). Each form must be genuinely inflected for its quantity; repeating one sentence across all forms is a bug in Slavic/Baltic languages, though it is correct for Turkish and Hungarian.
|
|
||||||
- An entry whose `msgstr` equals its `msgid` is untranslated even though it is not empty; a plural entry with any empty form is likewise incomplete.
|
|
||||||
- Mark machine-produced translations with an `# AI Translated` translator comment. Don't add it to a human translation you didn't actually rewrite.
|
|
||||||
- Don't reflow or re-wrap unrelated entries — keep the diff limited to the strings you changed.
|
|
||||||
|
|
||||||
### Verifying
|
|
||||||
|
|
||||||
- `scripts/run_gettext.bat --full` (Windows) regenerates the template, merges every catalog and compiles the `.mo` files. It must exit 0.
|
|
||||||
- Or check a single catalog with `msgfmt --check-format -o <out>.mo localization/i18n/<lang>/OrcaSlicer_<lang>.po`.
|
|
||||||
- Fuzzy entries are not shown to users. If you correct one, clear its `fuzzy` flag, otherwise the fix never ships.
|
|
||||||
|
|||||||
+148
-535
@@ -4,10 +4,6 @@ endif()
|
|||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
|
|
||||||
if(POLICY CMP0177)
|
|
||||||
cmake_policy(SET CMP0177 NEW)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
# The following line used to be in tests/CMakeLists.txt
|
# The following line used to be in tests/CMakeLists.txt
|
||||||
# Having it there causes rebuilds of all targets on any CMakeLists.txt change under tests/
|
# Having it there causes rebuilds of all targets on any CMakeLists.txt change under tests/
|
||||||
@@ -63,13 +59,6 @@ if (APPLE)
|
|||||||
message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Keep MSVC's default /W3 out of CMAKE_<LANG>_FLAGS so it can be applied to our own
|
|
||||||
# targets only. Silencing a bundled target would otherwise override a warning level,
|
|
||||||
# which cl reports as D9025 for every file it compiles.
|
|
||||||
if (POLICY CMP0092)
|
|
||||||
cmake_policy(SET CMP0092 NEW)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
project(OrcaSlicer)
|
project(OrcaSlicer)
|
||||||
|
|
||||||
# Backward compatibility for old CMake versions
|
# Backward compatibility for old CMake versions
|
||||||
@@ -91,14 +80,37 @@ endif()
|
|||||||
|
|
||||||
if (DEFINED BBL_RELEASE_TO_PUBLIC)
|
if (DEFINED BBL_RELEASE_TO_PUBLIC)
|
||||||
add_compile_definitions("BBL_RELEASE_TO_PUBLIC=${BBL_RELEASE_TO_PUBLIC}")
|
add_compile_definitions("BBL_RELEASE_TO_PUBLIC=${BBL_RELEASE_TO_PUBLIC}")
|
||||||
if (BBL_RELEASE_TO_PUBLIC)
|
|
||||||
add_compile_definitions(WXINSPECTOR_DISABLE)
|
|
||||||
endif ()
|
|
||||||
else ()
|
else ()
|
||||||
add_compile_definitions("BBL_RELEASE_TO_PUBLIC=$<CONFIG:Release>")
|
add_compile_definitions("BBL_RELEASE_TO_PUBLIC=$<CONFIG:Release>")
|
||||||
add_compile_definitions("$<$<CONFIG:Release>:WXINSPECTOR_DISABLE>")
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
find_package(Git)
|
||||||
|
if(DEFINED ENV{git_commit_hash} AND NOT "$ENV{git_commit_hash}" STREQUAL "")
|
||||||
|
message(STATUS "Specified git commit hash: $ENV{git_commit_hash}")
|
||||||
|
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||||
|
# Convert the given hash to short hash
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} rev-parse --short "$ENV{git_commit_hash}"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
# No .git directory (e.g., Flatpak sandbox) — truncate directly
|
||||||
|
string(SUBSTRING "$ENV{git_commit_hash}" 0 7 GIT_COMMIT_HASH)
|
||||||
|
endif()
|
||||||
|
add_definitions("-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
|
||||||
|
elseif(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||||
|
# Check current Git commit hash
|
||||||
|
execute_process(
|
||||||
|
COMMAND ${GIT_EXECUTABLE} log -1 --format=%h
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
||||||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
)
|
||||||
|
add_definitions("-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(DEFINED ENV{SLIC3R_STATIC})
|
if(DEFINED ENV{SLIC3R_STATIC})
|
||||||
set(SLIC3R_STATIC_INITIAL $ENV{SLIC3R_STATIC})
|
set(SLIC3R_STATIC_INITIAL $ENV{SLIC3R_STATIC})
|
||||||
else()
|
else()
|
||||||
@@ -107,141 +119,12 @@ endif()
|
|||||||
|
|
||||||
option(SLIC3R_STATIC "Compile OrcaSlicer with static libraries (Boost, TBB)" ${SLIC3R_STATIC_INITIAL})
|
option(SLIC3R_STATIC "Compile OrcaSlicer with static libraries (Boost, TBB)" ${SLIC3R_STATIC_INITIAL})
|
||||||
option(SLIC3R_GUI "Compile OrcaSlicer with GUI components (OpenGL, wxWidgets)" 1)
|
option(SLIC3R_GUI "Compile OrcaSlicer with GUI components (OpenGL, wxWidgets)" 1)
|
||||||
option(SLIC3R_CAD "Compile OrcaSlicer with the parametric Design/CAD tab (needs OCCT ModelingAlgorithms)" 1)
|
|
||||||
option(SLIC3R_FHS "Assume OrcaSlicer is to be installed in a FHS directory structure" 0)
|
option(SLIC3R_FHS "Assume OrcaSlicer is to be installed in a FHS directory structure" 0)
|
||||||
option(SLIC3R_PROFILE "Compile OrcaSlicer with an invasive Shiny profiler" 0)
|
option(SLIC3R_PROFILE "Compile OrcaSlicer with an invasive Shiny profiler" 0)
|
||||||
option(SLIC3R_PCH "Use precompiled headers" 1)
|
option(SLIC3R_PCH "Use precompiled headers" 1)
|
||||||
option(SLIC3R_WARNINGS "Emit compiler warnings for OrcaSlicer sources" 1)
|
|
||||||
option(SLIC3R_BUNDLED_WARNINGS "Emit compiler warnings for bundled third-party sources" 0)
|
|
||||||
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
|
option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1)
|
||||||
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
|
option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1)
|
||||||
option(SLIC3R_RELATIVE_DEBUG_PATHS "Record a relative compilation directory in debug info (clang-cl)" 0)
|
|
||||||
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
|
option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0)
|
||||||
|
|
||||||
# Python stubgen module
|
|
||||||
option(ORCA_BUILD_PYTHON_STUBGEN_MODULE "Build importable Python module for pybind11-stubgen" ON)
|
|
||||||
set(ORCA_BUNDLED_UV_EXECUTABLE "" CACHE FILEPATH "Path to a uv executable to bundle for Python package installation. Leave empty to auto-download.")
|
|
||||||
|
|
||||||
# Auto-download uv if not provided by the user.
|
|
||||||
# uv is pinned: the executable ships inside the signed app, so the build must
|
|
||||||
# be reproducible and the download verified. To upgrade, bump ORCA_UV_VERSION
|
|
||||||
# and refresh every hash below from the release's .sha256 assets, e.g.:
|
|
||||||
# curl -sL https://github.com/astral-sh/uv/releases/download/<ver>/uv-<triple>.tar.gz.sha256
|
|
||||||
set(ORCA_UV_VERSION "0.11.21")
|
|
||||||
set(ORCA_UV_SHA256_aarch64-apple-darwin "1f921d491ba5ffeea774eb04d6681ecee379101341cbb1500394993b541bf3f4")
|
|
||||||
set(ORCA_UV_SHA256_x86_64-apple-darwin "f3c8e5708a84b920c18b691214d54d2b0da6b984789caae95d47c95120cb7765")
|
|
||||||
set(ORCA_UV_SHA256_aarch64-unknown-linux-gnu "88e800834007cc5efd4675f166eb2a51e7e3ad19876d85fa8805a6fb5c922397")
|
|
||||||
set(ORCA_UV_SHA256_x86_64-unknown-linux-gnu "8c88519b0ef0af9801fcdee419bbb12116bd9e6b18e162ae093c932d8b264050")
|
|
||||||
set(ORCA_UV_SHA256_x86_64-pc-windows-msvc "ace861f360c6de2babedc1607d0f454b6b09a820dbc8182dc15af927e4df9589")
|
|
||||||
|
|
||||||
# Version-scoped cache dir so a version bump invalidates the cached binary.
|
|
||||||
set(ORCA_UV_DOWNLOAD_DIR "${CMAKE_BINARY_DIR}/.uv/${ORCA_UV_VERSION}")
|
|
||||||
set(ORCA_UV_BINARY "${ORCA_UV_DOWNLOAD_DIR}/uv")
|
|
||||||
if(WIN32)
|
|
||||||
set(ORCA_UV_BINARY "${ORCA_UV_DOWNLOAD_DIR}/uv.exe")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Older configures FORCE-cached the auto-downloaded path; any cache entry
|
|
||||||
# under .uv/ is that legacy value -- clear it so auto-download re-resolves
|
|
||||||
# (user-provided paths never live under .uv/).
|
|
||||||
if(ORCA_BUNDLED_UV_EXECUTABLE MATCHES "/\\.uv/")
|
|
||||||
set(ORCA_BUNDLED_UV_EXECUTABLE "" CACHE FILEPATH "Path to the auto-downloaded uv executable" FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(NOT ORCA_BUNDLED_UV_EXECUTABLE)
|
|
||||||
if(NOT EXISTS "${ORCA_UV_BINARY}")
|
|
||||||
# Select uv by TARGET arch. On macOS universal builds each leg sets a single
|
|
||||||
# CMAKE_OSX_ARCHITECTURES (and the x86_64 leg cross-builds on an arm64 runner),
|
|
||||||
# so prefer it over the host CMAKE_SYSTEM_PROCESSOR -- otherwise both legs fetch
|
|
||||||
# the same arch and the later universal lipo merge of tools/uv/uv fails.
|
|
||||||
set(_orca_uv_proc "${CMAKE_SYSTEM_PROCESSOR}")
|
|
||||||
if(APPLE AND CMAKE_OSX_ARCHITECTURES)
|
|
||||||
set(_orca_uv_proc "${CMAKE_OSX_ARCHITECTURES}")
|
|
||||||
endif()
|
|
||||||
# All release archives are tar.gz except the Windows zip.
|
|
||||||
set(ORCA_UV_EXT "tar.gz")
|
|
||||||
if(_orca_uv_proc MATCHES "x86_64|AMD64|amd64")
|
|
||||||
if(WIN32)
|
|
||||||
set(ORCA_UV_ARCH "x86_64-pc-windows-msvc")
|
|
||||||
set(ORCA_UV_EXT "zip")
|
|
||||||
elseif(APPLE)
|
|
||||||
set(ORCA_UV_ARCH "x86_64-apple-darwin")
|
|
||||||
else()
|
|
||||||
set(ORCA_UV_ARCH "x86_64-unknown-linux-gnu")
|
|
||||||
endif()
|
|
||||||
elseif(_orca_uv_proc MATCHES "aarch64|arm64|ARM64")
|
|
||||||
if(APPLE)
|
|
||||||
set(ORCA_UV_ARCH "aarch64-apple-darwin")
|
|
||||||
else()
|
|
||||||
set(ORCA_UV_ARCH "aarch64-unknown-linux-gnu")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(WARNING "Unsupported architecture for auto-downloading uv: ${_orca_uv_proc}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(ORCA_UV_ARCH)
|
|
||||||
set(ORCA_UV_URL "https://github.com/astral-sh/uv/releases/download/${ORCA_UV_VERSION}/uv-${ORCA_UV_ARCH}.${ORCA_UV_EXT}")
|
|
||||||
set(ORCA_UV_ARCHIVE "${ORCA_UV_DOWNLOAD_DIR}/uv-archive.${ORCA_UV_EXT}")
|
|
||||||
file(MAKE_DIRECTORY "${ORCA_UV_DOWNLOAD_DIR}")
|
|
||||||
message(STATUS "Downloading uv ${ORCA_UV_VERSION} from ${ORCA_UV_URL} ...")
|
|
||||||
file(DOWNLOAD "${ORCA_UV_URL}" "${ORCA_UV_ARCHIVE}" STATUS ORCA_UV_DL_STATUS TLS_VERIFY ON)
|
|
||||||
list(GET ORCA_UV_DL_STATUS 0 ORCA_UV_DL_CODE)
|
|
||||||
list(GET ORCA_UV_DL_STATUS 1 ORCA_UV_DL_MSG)
|
|
||||||
if(NOT ORCA_UV_DL_CODE EQUAL 0)
|
|
||||||
# Network failure keeps the historical graceful degradation (uv is
|
|
||||||
# optional at build time) -- but a hash mismatch below is fatal.
|
|
||||||
message(WARNING "Failed to download uv: ${ORCA_UV_DL_MSG}")
|
|
||||||
file(REMOVE "${ORCA_UV_ARCHIVE}")
|
|
||||||
else()
|
|
||||||
file(SHA256 "${ORCA_UV_ARCHIVE}" _orca_uv_actual_sha256)
|
|
||||||
if(NOT _orca_uv_actual_sha256 STREQUAL "${ORCA_UV_SHA256_${ORCA_UV_ARCH}}")
|
|
||||||
file(REMOVE "${ORCA_UV_ARCHIVE}")
|
|
||||||
message(FATAL_ERROR
|
|
||||||
"uv archive checksum mismatch for ${ORCA_UV_ARCH} ${ORCA_UV_VERSION}:\n"
|
|
||||||
" expected ${ORCA_UV_SHA256_${ORCA_UV_ARCH}}\n"
|
|
||||||
" actual ${_orca_uv_actual_sha256}\n"
|
|
||||||
"Possible tampering or a stale pin; refusing to bundle.")
|
|
||||||
endif()
|
|
||||||
message(STATUS "Extracting uv ...")
|
|
||||||
file(ARCHIVE_EXTRACT INPUT "${ORCA_UV_ARCHIVE}" DESTINATION "${ORCA_UV_DOWNLOAD_DIR}")
|
|
||||||
file(REMOVE "${ORCA_UV_ARCHIVE}")
|
|
||||||
# Pinned archives have a fixed layout: the tarballs hold
|
|
||||||
# uv-<triple>/uv, the Windows zip holds uv.exe at the root
|
|
||||||
# (already at ORCA_UV_BINARY). A layout change implies a new
|
|
||||||
# archive, hence a hash bump -- and RENAME fails loudly.
|
|
||||||
if(NOT WIN32)
|
|
||||||
file(RENAME "${ORCA_UV_DOWNLOAD_DIR}/uv-${ORCA_UV_ARCH}/uv" "${ORCA_UV_BINARY}")
|
|
||||||
file(REMOVE_RECURSE "${ORCA_UV_DOWNLOAD_DIR}/uv-${ORCA_UV_ARCH}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(EXISTS "${ORCA_UV_BINARY}")
|
|
||||||
# Plain set (shadows the empty cache entry for this configure run):
|
|
||||||
# the version-scoped path re-derives every configure, so a version
|
|
||||||
# bump takes effect in warm build dirs without cache surgery.
|
|
||||||
set(ORCA_BUNDLED_UV_EXECUTABLE "${ORCA_UV_BINARY}")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Resolve ORCA_BUNDLED_UV_EXECUTABLE -> ORCA_BUNDLED_UV_EXECUTABLE_CONFIG / ORCA_BUNDLED_UV_FILENAME
|
|
||||||
# Must run before add_subdirectory(src) so target_compile_definitions sees it.
|
|
||||||
if(ORCA_BUNDLED_UV_EXECUTABLE)
|
|
||||||
if(EXISTS "${ORCA_BUNDLED_UV_EXECUTABLE}")
|
|
||||||
file(TO_CMAKE_PATH "${ORCA_BUNDLED_UV_EXECUTABLE}" ORCA_BUNDLED_UV_EXECUTABLE_CONFIG)
|
|
||||||
get_filename_component(ORCA_BUNDLED_UV_FILENAME "${ORCA_BUNDLED_UV_EXECUTABLE}" NAME)
|
|
||||||
else()
|
|
||||||
message(WARNING "ORCA_BUNDLED_UV_EXECUTABLE does not exist: ${ORCA_BUNDLED_UV_EXECUTABLE}")
|
|
||||||
set(ORCA_BUNDLED_UV_EXECUTABLE "")
|
|
||||||
set(ORCA_BUNDLED_UV_EXECUTABLE_CONFIG "")
|
|
||||||
set(ORCA_BUNDLED_UV_FILENAME "")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
set(ORCA_BUNDLED_UV_EXECUTABLE_CONFIG "")
|
|
||||||
set(ORCA_BUNDLED_UV_FILENAME "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
|
# If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable.
|
||||||
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow performing desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
|
CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow performing desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0)
|
||||||
|
|
||||||
@@ -277,8 +160,6 @@ if (APPLE)
|
|||||||
SET(CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.orcaslicer.OrcaSlicer")
|
SET(CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.orcaslicer.OrcaSlicer")
|
||||||
|
|
||||||
message(STATUS "Orca: IS_CROSS_COMPILE: ${IS_CROSS_COMPILE}")
|
message(STATUS "Orca: IS_CROSS_COMPILE: ${IS_CROSS_COMPILE}")
|
||||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Proposal for C++ unit tests and sandboxes
|
# Proposal for C++ unit tests and sandboxes
|
||||||
@@ -309,10 +190,6 @@ if (SLIC3R_GUI)
|
|||||||
add_definitions(-DSLIC3R_GUI)
|
add_definitions(-DSLIC3R_GUI)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (SLIC3R_CAD)
|
|
||||||
add_definitions(-DSLIC3R_CAD)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if(SLIC3R_DESKTOP_INTEGRATION)
|
if(SLIC3R_DESKTOP_INTEGRATION)
|
||||||
add_definitions(-DSLIC3R_DESKTOP_INTEGRATION)
|
add_definitions(-DSLIC3R_DESKTOP_INTEGRATION)
|
||||||
endif ()
|
endif ()
|
||||||
@@ -329,47 +206,21 @@ if (MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|||||||
|
|
||||||
# clang-cl can interpret SYSTEM header paths if -imsvc is used
|
# clang-cl can interpret SYSTEM header paths if -imsvc is used
|
||||||
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc")
|
set(CMAKE_INCLUDE_SYSTEM_FLAG_CXX "-imsvc")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall \
|
||||||
|
-Wno-old-style-cast -Wno-reserved-id-macro -Wno-c++98-compat-pedantic")
|
||||||
else ()
|
else ()
|
||||||
set(IS_CLANG_CL FALSE)
|
set(IS_CLANG_CL FALSE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
# CMP0092 only applies when the cache is created; an existing tree keeps its /W3,
|
if (SLIC3R_MSVC_COMPILE_PARALLEL AND NOT IS_CLANG_CL)
|
||||||
# which a silenced bundled target would then override (D9025, once per file).
|
|
||||||
string(REGEX REPLACE "/W[0-4]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
|
||||||
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
||||||
|
|
||||||
# /MP only matters for the VS generators, where CMake turns it into the
|
|
||||||
# MultiProcessorCompilation property. Ninja parallelises on its own, and
|
|
||||||
# clang-cl warns "argument unused" if the flag reaches it.
|
|
||||||
if (SLIC3R_MSVC_COMPILE_PARALLEL AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
|
||||||
add_compile_options(/MP)
|
add_compile_options(/MP)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Parse lambdas the way the standard says, as clang and GCC already do. Without it
|
|
||||||
# MSVC keeps its legacy lambda processor under /std:c++17 and rejects reading a
|
|
||||||
# constexpr constant inside a lambda that does not capture it (C3493), which no
|
|
||||||
# other compiler requires. Implied by /std:c++20 and /permissive-, so it is only
|
|
||||||
# needed while we are on C++17. clang-cl is conforming already and does not take
|
|
||||||
# the flag. Requires VS2019 16.8 or newer.
|
|
||||||
if (NOT IS_CLANG_CL)
|
|
||||||
# cl.exe only warns (D9002) about an unknown /Zc: option, so without this the
|
|
||||||
# flag would be dropped and the first lambda reading a constexpr constant would
|
|
||||||
# fail with C3493 far from the cause.
|
|
||||||
if (MSVC_VERSION LESS 1928)
|
|
||||||
message(FATAL_ERROR "Visual Studio 2019 16.8 (MSVC 19.28) or newer is required; detected MSVC ${MSVC_VERSION}.")
|
|
||||||
endif ()
|
|
||||||
add_compile_options(/Zc:lambda)
|
|
||||||
endif ()
|
|
||||||
# /bigobj (Increase Number of Sections in .Obj file)
|
# /bigobj (Increase Number of Sections in .Obj file)
|
||||||
add_compile_options(-bigobj)
|
|
||||||
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
|
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
|
||||||
# Generate symbols at every build target, even for the release.
|
# Generate symbols at every build target, even for the release.
|
||||||
# -Zm520 fixes error C3859 but forces the compiler to pre-allocate that memory for every translation unit regardless
|
add_compile_options(-bigobj -Zm520 /Zi)
|
||||||
# combining /Zi with /FS frees up a significant amount of memory pressure across all parallel compile jobs and makes /MP faster overall.
|
|
||||||
if (SLIC3R_MSVC_PDB)
|
|
||||||
add_compile_options(/Zi /FS)
|
|
||||||
endif ()
|
|
||||||
# Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17.
|
# Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17.
|
||||||
#FIXME Remove this line after eigen library adapts to the new C++17 adaptor rules.
|
#FIXME Remove this line after eigen library adapts to the new C++17 adaptor rules.
|
||||||
add_compile_options(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING)
|
add_compile_options(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING)
|
||||||
@@ -380,21 +231,6 @@ if (MSVC)
|
|||||||
# Disable warnings on comparison of unsigned and signed
|
# Disable warnings on comparison of unsigned and signed
|
||||||
# C4018: signed/unsigned mismatch
|
# C4018: signed/unsigned mismatch
|
||||||
add_compile_options(/wd4018)
|
add_compile_options(/wd4018)
|
||||||
# Prevent linker restart when TBB (or other deps) built with /GL are linked
|
|
||||||
# Make your full (clean) build slower because it enables link-time code generation across all translation units.
|
|
||||||
# helps incremental builds by preventing the double-link restart from TBB
|
|
||||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LTCG")
|
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /LTCG")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Without this every object names its build directory and two worktrees never
|
|
||||||
# share cache entries. The linker still writes absolute paths into the PDB.
|
|
||||||
if (SLIC3R_RELATIVE_DEBUG_PATHS)
|
|
||||||
if (IS_CLANG_CL)
|
|
||||||
add_compile_options(-ffile-compilation-dir=.)
|
|
||||||
else ()
|
|
||||||
message(WARNING "SLIC3R_RELATIVE_DEBUG_PATHS is only implemented for clang-cl")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang" AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 15)
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang" AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 15)
|
||||||
@@ -551,103 +387,63 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUXX)
|
|||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals" )
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fext-numeric-literals" )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ((NOT MSVC OR IS_CLANG_CL) AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang"))
|
||||||
if (IS_CLANG_CL)
|
if (NOT MINGW)
|
||||||
# clang-cl reads -Wall as MSVC /Wall, which clang maps to -Weverything. /W4 is
|
|
||||||
# its -Wall -Wextra and, unlike /clang:-Wall, is ordered with the -Wno-* below
|
|
||||||
# instead of after them. The -Wextra-only warnings are dropped again so the set
|
|
||||||
# matches what -Wall gives the GNU/Clang builds.
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4" )
|
|
||||||
add_compile_options(-Wno-unused-parameter -Wno-ignored-qualifiers -Wno-missing-field-initializers)
|
|
||||||
elseif (NOT MINGW)
|
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall" )
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall" )
|
||||||
endif ()
|
endif ()
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reorder" )
|
||||||
|
|
||||||
# Every warning is an error unless it appears in one of the two lists below.
|
# On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error.
|
||||||
# disabled - never wanted. Off everywhere, so it never warns or errors.
|
add_compile_options(-Werror=return-type)
|
||||||
# demoted - wanted, not cleared yet. Still warns, does not error.
|
|
||||||
|
|
||||||
# Disabled.
|
# Since some portions of code are just commented out or put under conditional compilation, there are
|
||||||
set(warnings_disabled
|
# a bunch of warning related to unused functions and variables. Suppress those warnings to not pollute
|
||||||
reorder # members initialised in an order we chose
|
# compilers diagnostics output with warnings we not going to look at
|
||||||
sign-compare # signed/unsigned comparisons throughout
|
add_compile_options(-Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-label -Wno-unused-local-typedefs)
|
||||||
misleading-indentation # false positives on mixed tabs and spaces
|
|
||||||
switch # unhandled enum value in a switch
|
|
||||||
unused-function # commented-out or conditionally compiled code
|
|
||||||
unused-variable # commented-out or conditionally compiled code
|
|
||||||
unused-but-set-variable # commented-out or conditionally compiled code
|
|
||||||
unused-label # commented-out or conditionally compiled code
|
|
||||||
unused-local-typedefs # commented-out or conditionally compiled code
|
|
||||||
)
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
list(APPEND warnings_disabled deprecated-declarations) # legacy OpenGL calls
|
|
||||||
endif ()
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0)
|
|
||||||
list(APPEND warnings_disabled ignored-attributes) # from Eigen headers marked SYSTEM
|
|
||||||
endif ()
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
||||||
list(APPEND warnings_disabled unknown-pragmas) # igl pragmas, GCC bug 66943
|
|
||||||
endif ()
|
|
||||||
foreach (w IN LISTS warnings_disabled)
|
|
||||||
add_compile_options(-Wno-${w})
|
|
||||||
endforeach ()
|
|
||||||
|
|
||||||
# GCC is not built in CI, so don't throw errors CI won't catch.
|
# Ignore signed/unsigned comparison warnings
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
add_compile_options(-Wno-sign-compare)
|
||||||
add_compile_options(-Werror=return-type)
|
|
||||||
else ()
|
|
||||||
# Turn everything else into an error. Dependency headers are exempt because the
|
|
||||||
# SYSTEM include flag (-imsvc on clang-cl, -isystem elsewhere) keeps their
|
|
||||||
# diagnostics out.
|
|
||||||
add_compile_options(-Werror)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Demoted. Remove a name once its category is cleared on every compiler.
|
# The mismatch of tabs and spaces throughout the project can sometimes
|
||||||
set(warnings_demoted)
|
# cause this warning to appear even though the indentation is fine.
|
||||||
if (APPLE)
|
# Some includes also cause the warning
|
||||||
list(APPEND warnings_demoted
|
add_compile_options(-Wno-misleading-indentation)
|
||||||
# MacDarkMode.mm makes two calls to AppKit's private titlebarViewController
|
|
||||||
# and one to a wxWidgets category on NSTableColumn whose header is not
|
|
||||||
# imported. Clearing it means declaring the private selectors ourselves, which
|
|
||||||
# needs a macOS build to verify.
|
|
||||||
objc-method-access
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
if (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
|
|
||||||
list(APPEND warnings_demoted
|
|
||||||
# About two dozen GetProcAddress casts, most in the vendored dark_mode.hpp,
|
|
||||||
# retype FARPROC to a real signature. The __stdcall typedefs are identical to
|
|
||||||
# FARPROC on x64, so only arm64 reports them. Clearing them is a separate
|
|
||||||
# sweep.
|
|
||||||
cast-function-type-mismatch
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
list(APPEND warnings_demoted
|
|
||||||
# enum-constexpr-conversion is a Clang warning that defaults to an error,
|
|
||||||
# present through clang 20 and gone in clang 21.
|
|
||||||
enum-constexpr-conversion
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# The list mixes names not every compiler has, so add each exception only where the
|
# Disable warning if enum value does not have a corresponding case in switch statement
|
||||||
# compiler knows the warning. Probe with the positive -W<name>, which an unknown
|
add_compile_options(-Wno-switch)
|
||||||
# warning fails on both compilers (GCC errors, Clang reports unknown-warning-option).
|
|
||||||
# An option that takes a =N argument rejects the bare -W<name>, so fall back to
|
# removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1)
|
||||||
# -W<name>=1 and demote with the trailing =.
|
# https://eigen.tuxfamily.org/bz/show_bug.cgi?id=1221
|
||||||
include(CheckCXXCompilerFlag)
|
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0)
|
||||||
foreach (category IN LISTS warnings_demoted)
|
add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM
|
||||||
string(MAKE_C_IDENTIFIER "ORCA_HAS_W_${category}" _orca_has_w)
|
endif()
|
||||||
check_cxx_compiler_flag("-W${category}" ${_orca_has_w})
|
|
||||||
if (${_orca_has_w})
|
# Clang reports legacy OpenGL calls as deprecated. Turn off the warning for now
|
||||||
add_compile_options(-Wno-error=${category})
|
# to reduce the clutter, we know about this one. It should be reenabled after
|
||||||
else ()
|
# we finally get rid of the deprecated code.
|
||||||
check_cxx_compiler_flag("-W${category}=1" ${_orca_has_w}_arg)
|
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
if (${${_orca_has_w}_arg})
|
add_compile_options(-Wno-deprecated-declarations)
|
||||||
add_compile_options(-Wno-error=${category}=)
|
endif()
|
||||||
endif ()
|
|
||||||
endif ()
|
if((${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 15)
|
||||||
endforeach ()
|
include(CheckCXXCompilerFlag)
|
||||||
|
check_cxx_compiler_flag(-Wno-error=enum-constexpr-conversion HAS_WNO_ERROR_ENUM_CONSTEXPR_CONV)
|
||||||
|
if(HAS_WNO_ERROR_ENUM_CONSTEXPR_CONV)
|
||||||
|
add_compile_options(-Wno-error=enum-constexpr-conversion)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
|
||||||
|
# We will turn the warning of for GCC for now:
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
|
# GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or
|
||||||
|
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
|
||||||
|
# We will turn the warning of for GCC for now:
|
||||||
|
add_compile_options(-Wno-unknown-pragmas)
|
||||||
|
endif()
|
||||||
|
|
||||||
# Compress the debug info with zstd to save space in Flatpak CI builds
|
# Compress the debug info with zstd to save space in Flatpak CI builds
|
||||||
if(FLATPAK)
|
if(FLATPAK)
|
||||||
@@ -657,6 +453,10 @@ if ((NOT MSVC OR IS_CLANG_CL) AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 14)
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error=template-id-cdtor" )
|
||||||
|
endif()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (SLIC3R_ASAN)
|
if (SLIC3R_ASAN)
|
||||||
@@ -903,7 +703,7 @@ endif ()
|
|||||||
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/i18n")
|
set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/i18n")
|
||||||
set(BBL_L18N_DIR "${CMAKE_CURRENT_SOURCE_DIR}/localization/i18n")
|
set(BBL_L18N_DIR "${CMAKE_CURRENT_SOURCE_DIR}/localization/i18n")
|
||||||
add_custom_target(gettext_make_pot
|
add_custom_target(gettext_make_pot
|
||||||
COMMAND 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
|
COMMAND 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
|
||||||
-f "${BBL_L18N_DIR}/list.txt"
|
-f "${BBL_L18N_DIR}/list.txt"
|
||||||
-o "${BBL_L18N_DIR}/OrcaSlicer.pot"
|
-o "${BBL_L18N_DIR}/OrcaSlicer.pot"
|
||||||
COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${BBL_L18N_DIR}
|
COMMAND hintsToPot ${SLIC3R_RESOURCES_DIR} ${BBL_L18N_DIR}
|
||||||
@@ -921,6 +721,7 @@ foreach(po_file ${BBL_L10N_PO_FILES})
|
|||||||
add_custom_command(
|
add_custom_command(
|
||||||
TARGET gettext_merge_po_with_pot PRE_BUILD
|
TARGET gettext_merge_po_with_pot PRE_BUILD
|
||||||
COMMAND msgmerge -N -o ${po_file} ${po_file} "${BBL_L18N_DIR}/OrcaSlicer.pot"
|
COMMAND msgmerge -N -o ${po_file} ${po_file} "${BBL_L18N_DIR}/OrcaSlicer.pot"
|
||||||
|
DEPENDS ${po_file}
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
add_custom_target(gettext_po_to_mo
|
add_custom_target(gettext_po_to_mo
|
||||||
@@ -936,91 +737,12 @@ foreach(po_file ${BBL_L10N_PO_FILES})
|
|||||||
TARGET gettext_po_to_mo PRE_BUILD
|
TARGET gettext_po_to_mo PRE_BUILD
|
||||||
COMMAND msgfmt ARGS --check-format -o ${mo_file} ${po_file}
|
COMMAND msgfmt ARGS --check-format -o ${mo_file} ${po_file}
|
||||||
#COMMAND msgfmt ARGS --check-compatibility -o ${mo_file} ${po_file}
|
#COMMAND msgfmt ARGS --check-compatibility -o ${mo_file} ${po_file}
|
||||||
|
DEPENDS ${po_file}
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
find_package(NLopt 1.4 REQUIRED)
|
find_package(NLopt 1.4 REQUIRED)
|
||||||
|
|
||||||
# Use bundled Python from deps instead of system Python
|
|
||||||
set(_bundled_python_version "3.12.13")
|
|
||||||
set(_bundled_python_abi "312")
|
|
||||||
string(REGEX REPLACE "^([0-9]+\\.[0-9]+)\\..*$" "\\1" _bundled_python_version_short "${_bundled_python_version}")
|
|
||||||
set(_bundled_python_root "${CMAKE_PREFIX_PATH}/libpython")
|
|
||||||
set(Python3_ROOT_DIR "${_bundled_python_root}" CACHE PATH "Root directory for bundled Python" FORCE)
|
|
||||||
set(Python3_USE_STATIC_LIBS OFF)
|
|
||||||
set(Python3_FIND_STRATEGY LOCATION)
|
|
||||||
set(Python3_FIND_IMPLEMENTATIONS CPython)
|
|
||||||
|
|
||||||
if(WIN32)
|
|
||||||
set(_bundled_python_executable "${_bundled_python_root}/python.exe")
|
|
||||||
set(_bundled_python_library "${_bundled_python_root}/libs/python${_bundled_python_abi}.lib")
|
|
||||||
if(EXISTS "${_bundled_python_root}/python_d.exe")
|
|
||||||
set(_bundled_python_executable "${_bundled_python_root}/python_d.exe")
|
|
||||||
endif()
|
|
||||||
if(EXISTS "${_bundled_python_root}/libs/python${_bundled_python_abi}_d.lib")
|
|
||||||
set(_bundled_python_library "${_bundled_python_root}/libs/python${_bundled_python_abi}_d.lib")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(Python3_FIND_REGISTRY NEVER)
|
|
||||||
set(Python3_EXECUTABLE "${_bundled_python_executable}" CACHE FILEPATH "Bundled Python executable" FORCE)
|
|
||||||
set(Python3_INCLUDE_DIR "${_bundled_python_root}/include" CACHE PATH "Bundled Python include directory" FORCE)
|
|
||||||
set(Python3_LIBRARY "${_bundled_python_library}" CACHE FILEPATH "Bundled Python embed import library" FORCE)
|
|
||||||
elseif(APPLE)
|
|
||||||
set(Python3_FIND_FRAMEWORK NEVER)
|
|
||||||
|
|
||||||
find_program(_bundled_python_executable
|
|
||||||
NAMES python${_bundled_python_version_short} python3
|
|
||||||
PATHS "${_bundled_python_root}/bin"
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
)
|
|
||||||
find_path(_bundled_python_include_dir
|
|
||||||
NAMES Python.h
|
|
||||||
PATHS
|
|
||||||
"${_bundled_python_root}/include/python${_bundled_python_version_short}"
|
|
||||||
"${_bundled_python_root}/include"
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
)
|
|
||||||
find_library(_bundled_python_library
|
|
||||||
NAMES python${_bundled_python_version_short} libpython${_bundled_python_version_short}
|
|
||||||
PATHS "${_bundled_python_root}/lib"
|
|
||||||
NO_DEFAULT_PATH
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT _bundled_python_executable)
|
|
||||||
message(FATAL_ERROR "Bundled Python executable not found under ${_bundled_python_root}/bin")
|
|
||||||
endif()
|
|
||||||
if(NOT _bundled_python_include_dir)
|
|
||||||
message(FATAL_ERROR "Bundled Python headers not found under ${_bundled_python_root}/include")
|
|
||||||
endif()
|
|
||||||
if(NOT _bundled_python_library)
|
|
||||||
message(FATAL_ERROR "Bundled Python library not found under ${_bundled_python_root}/lib")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(Python3_EXECUTABLE "${_bundled_python_executable}" CACHE FILEPATH "Bundled Python executable" FORCE)
|
|
||||||
set(Python3_INCLUDE_DIR "${_bundled_python_include_dir}" CACHE PATH "Bundled Python include directory" FORCE)
|
|
||||||
set(Python3_LIBRARY "${_bundled_python_library}" CACHE FILEPATH "Bundled Python embed library" FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(Python3 ${_bundled_python_version} EXACT REQUIRED
|
|
||||||
COMPONENTS Interpreter Development.Embed
|
|
||||||
)
|
|
||||||
|
|
||||||
# Provide a minimal pybind11::embed target sourced from deps_src/pybind11 headers.
|
|
||||||
set(PYBIND11_SOURCE_DIR "${CMAKE_SOURCE_DIR}/deps_src/pybind11")
|
|
||||||
if(NOT EXISTS "${PYBIND11_SOURCE_DIR}/include/pybind11/pybind11.h")
|
|
||||||
message(FATAL_ERROR "pybind11 headers not found in ${PYBIND11_SOURCE_DIR}. Did you initialize submodules?")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(pybind11_headers INTERFACE)
|
|
||||||
target_include_directories(pybind11_headers INTERFACE "${PYBIND11_SOURCE_DIR}/include")
|
|
||||||
add_library(pybind11::headers ALIAS pybind11_headers)
|
|
||||||
add_library(pybind11::pybind11 ALIAS pybind11_headers)
|
|
||||||
|
|
||||||
add_library(pybind11_embed INTERFACE)
|
|
||||||
target_link_libraries(pybind11_embed INTERFACE pybind11_headers Python3::Python)
|
|
||||||
target_compile_definitions(pybind11_embed INTERFACE PYBIND11_SIMPLE_GIL_MANAGEMENT)
|
|
||||||
add_library(pybind11::embed ALIAS pybind11_embed)
|
|
||||||
|
|
||||||
|
|
||||||
if(SLIC3R_STATIC)
|
if(SLIC3R_STATIC)
|
||||||
set(OPENVDB_USE_STATIC_LIBS ON)
|
set(OPENVDB_USE_STATIC_LIBS ON)
|
||||||
@@ -1081,150 +803,77 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
|
|||||||
${TOP_LEVEL_PROJECT_DIR}/deps/WebView2/lib/win-${_arch}/WebView2Loader.dll
|
${TOP_LEVEL_PROJECT_DIR}/deps/WebView2/lib/win-${_arch}/WebView2Loader.dll
|
||||||
DESTINATION ${_out_dir})
|
DESTINATION ${_out_dir})
|
||||||
|
|
||||||
# Stage the OCCT toolkits libslic3r links (published as OCCT_LIBS), not whatever the
|
file(COPY ${CMAKE_PREFIX_PATH}/bin/occt/TKBO.dll
|
||||||
# deps prefix happens to hold, and fail the configure if one of them is missing.
|
${CMAKE_PREFIX_PATH}/bin/occt/TKBRep.dll
|
||||||
if (NOT OCCT_LIBS)
|
${CMAKE_PREFIX_PATH}/bin/occt/TKCAF.dll
|
||||||
message(FATAL_ERROR "OCCT_LIBS is not set; libslic3r must be configured first.")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKCDF.dll
|
||||||
endif ()
|
${CMAKE_PREFIX_PATH}/bin/occt/TKernel.dll
|
||||||
set(_occt_bin "${CMAKE_PREFIX_PATH}/bin/occt")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKG2d.dll
|
||||||
set(_occt_dlls "")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKG3d.dll
|
||||||
set(_occt_staged "")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKGeomAlgo.dll
|
||||||
set(_missing_occt "")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKGeomBase.dll
|
||||||
foreach (_tk IN LISTS OCCT_LIBS)
|
${CMAKE_PREFIX_PATH}/bin/occt/TKHLR.dll
|
||||||
if (EXISTS "${_occt_bin}/${_tk}.dll")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKLCAF.dll
|
||||||
list(APPEND _occt_dlls "${_occt_bin}/${_tk}.dll")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKMath.dll
|
||||||
list(APPEND _occt_staged "${_out_dir}/${_tk}.dll")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKMesh.dll
|
||||||
else ()
|
${CMAKE_PREFIX_PATH}/bin/occt/TKPrim.dll
|
||||||
list(APPEND _missing_occt "${_tk}.dll")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKService.dll
|
||||||
endif ()
|
${CMAKE_PREFIX_PATH}/bin/occt/TKShHealing.dll
|
||||||
endforeach ()
|
${CMAKE_PREFIX_PATH}/bin/occt/TKSTEP.dll
|
||||||
if (_missing_occt)
|
${CMAKE_PREFIX_PATH}/bin/occt/TKSTEP209.dll
|
||||||
message(FATAL_ERROR
|
${CMAKE_PREFIX_PATH}/bin/occt/TKSTEPAttr.dll
|
||||||
"OCCT DLLs missing from ${_occt_bin}/: ${_missing_occt}\n"
|
${CMAKE_PREFIX_PATH}/bin/occt/TKSTEPBase.dll
|
||||||
"Rebuild the dependencies (build_release_vs2022.bat deps) with the same "
|
${CMAKE_PREFIX_PATH}/bin/occt/TKTopAlgo.dll
|
||||||
"SLIC3R_CAD setting as this project.")
|
${CMAKE_PREFIX_PATH}/bin/occt/TKV3d.dll
|
||||||
endif ()
|
${CMAKE_PREFIX_PATH}/bin/occt/TKVCAF.dll
|
||||||
file(COPY ${_occt_dlls}
|
${CMAKE_PREFIX_PATH}/bin/occt/TKXCAF.dll
|
||||||
|
${CMAKE_PREFIX_PATH}/bin/occt/TKXDESTEP.dll
|
||||||
|
${CMAKE_PREFIX_PATH}/bin/occt/TKXSBase.dll
|
||||||
${CMAKE_PREFIX_PATH}/bin/freetype.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})
|
DESTINATION ${_out_dir})
|
||||||
|
|
||||||
set(_dll_list
|
set(${output_dlls}
|
||||||
${_out_dir}/libgmp-10.dll
|
${_out_dir}/libgmp-10.dll
|
||||||
${_out_dir}/libmpfr-4.dll
|
${_out_dir}/libmpfr-4.dll
|
||||||
${_out_dir}/WebView2Loader.dll
|
${_out_dir}/WebView2Loader.dll
|
||||||
|
|
||||||
|
${_out_dir}/TKBO.dll
|
||||||
|
${_out_dir}/TKBRep.dll
|
||||||
|
${_out_dir}/TKCAF.dll
|
||||||
|
${_out_dir}/TKCDF.dll
|
||||||
|
${_out_dir}/TKernel.dll
|
||||||
|
${_out_dir}/TKG2d.dll
|
||||||
|
${_out_dir}/TKG3d.dll
|
||||||
|
${_out_dir}/TKGeomAlgo.dll
|
||||||
|
${_out_dir}/TKGeomBase.dll
|
||||||
|
${_out_dir}/TKHLR.dll
|
||||||
|
${_out_dir}/TKLCAF.dll
|
||||||
|
${_out_dir}/TKMath.dll
|
||||||
|
${_out_dir}/TKMesh.dll
|
||||||
|
${_out_dir}/TKPrim.dll
|
||||||
|
${_out_dir}/TKService.dll
|
||||||
|
${_out_dir}/TKShHealing.dll
|
||||||
|
${_out_dir}/TKSTEP.dll
|
||||||
|
${_out_dir}/TKSTEP209.dll
|
||||||
|
${_out_dir}/TKSTEPAttr.dll
|
||||||
|
${_out_dir}/TKSTEPBase.dll
|
||||||
|
${_out_dir}/TKTopAlgo.dll
|
||||||
|
${_out_dir}/TKV3d.dll
|
||||||
|
${_out_dir}/TKVCAF.dll
|
||||||
|
${_out_dir}/TKXCAF.dll
|
||||||
|
${_out_dir}/TKXDESTEP.dll
|
||||||
|
${_out_dir}/TKXSBase.dll
|
||||||
|
|
||||||
${_out_dir}/freetype.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
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
list(APPEND _dll_list ${_occt_staged})
|
|
||||||
set(${output_dlls} ${_dll_list} PARENT_SCOPE)
|
|
||||||
|
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
function(orcaslicer_copy_sos target config postfix output_sos)
|
|
||||||
|
|
||||||
get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
|
||||||
get_target_property(_alt_out_dir ${target} RUNTIME_OUTPUT_DIRECTORY)
|
|
||||||
|
|
||||||
if (_alt_out_dir)
|
|
||||||
set(_out_dir "${_alt_out_dir}")
|
|
||||||
elseif (_is_multi)
|
|
||||||
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}/${config}")
|
|
||||||
else ()
|
|
||||||
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
file(COPY ${CMAKE_PREFIX_PATH}/lib/libavcodec.so
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61.3.100
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libavutil.so
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libavutil.so.59
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libavutil.so.59.8.100
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libswscale.so
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libswscale.so.8
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libswscale.so.8.1.100
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libswresample.so
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libswresample.so.5
|
|
||||||
${CMAKE_PREFIX_PATH}/lib/libswresample.so.5.1.100
|
|
||||||
DESTINATION ${_out_dir})
|
|
||||||
|
|
||||||
set(${output_sos}
|
|
||||||
${_out_dir}/libavcodec.so
|
|
||||||
${_out_dir}/libavcodec.so.61
|
|
||||||
${_out_dir}/libavcodec.so.61.3.100
|
|
||||||
${_out_dir}/libavutil.so
|
|
||||||
${_out_dir}/libavutil.so.59
|
|
||||||
${_out_dir}/libavutil.so.59.8.100
|
|
||||||
${_out_dir}/libswscale.so
|
|
||||||
${_out_dir}/libswscale.so.8
|
|
||||||
${_out_dir}/libswscale.so.8.1.100
|
|
||||||
${_out_dir}/libswresample.so
|
|
||||||
${_out_dir}/libswresample.so.5
|
|
||||||
${_out_dir}/libswresample.so.5.1.100
|
|
||||||
PARENT_SCOPE
|
|
||||||
)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Bundled sources set their own warning flags, and a plain -Wall there means /Wall
|
|
||||||
# (= -Weverything) under clang-cl. Target options are applied after the ones a target
|
|
||||||
# set on itself, so these win. Targets are discovered rather than listed so a newly
|
|
||||||
# bundled library needs no maintenance here.
|
|
||||||
function(orcaslicer_silence_third_party_warnings _dir)
|
|
||||||
get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES)
|
|
||||||
foreach (_subdir IN LISTS _subdirs)
|
|
||||||
orcaslicer_silence_third_party_warnings("${_subdir}")
|
|
||||||
endforeach ()
|
|
||||||
get_property(_targets DIRECTORY "${_dir}" PROPERTY BUILDSYSTEM_TARGETS)
|
|
||||||
foreach (_target IN LISTS _targets)
|
|
||||||
get_target_property(_type ${_target} TYPE)
|
|
||||||
if (NOT _type STREQUAL "INTERFACE_LIBRARY" AND NOT _type STREQUAL "UTILITY")
|
|
||||||
if (MSVC AND NOT IS_CLANG_CL)
|
|
||||||
# Drop any level the target set for itself, or -w overrides it and cl
|
|
||||||
# reports D9025 once per file.
|
|
||||||
get_target_property(_opts ${_target} COMPILE_OPTIONS)
|
|
||||||
if (_opts)
|
|
||||||
string(REGEX REPLACE "/W[0-4]|/Wall" "" _opts "${_opts}")
|
|
||||||
string(REGEX REPLACE ";;+" ";" _opts "${_opts}")
|
|
||||||
set_target_properties(${_target} PROPERTIES COMPILE_OPTIONS "${_opts}")
|
|
||||||
endif ()
|
|
||||||
# CMake maps a level into the VS generator's WarningLevel element, while a
|
|
||||||
# bare -w stays on the command line and trips D9025 there, once per file.
|
|
||||||
target_compile_options(${_target} PRIVATE /W0)
|
|
||||||
else ()
|
|
||||||
target_compile_options(${_target} PRIVATE -w)
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
endforeach ()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
|
|
||||||
# libslic3r, OrcaSlicer GUI and the OrcaSlicer executable.
|
# libslic3r, OrcaSlicer GUI and the OrcaSlicer executable.
|
||||||
add_subdirectory(deps_src)
|
add_subdirectory(deps_src)
|
||||||
|
|
||||||
if (NOT SLIC3R_BUNDLED_WARNINGS)
|
|
||||||
orcaslicer_silence_third_party_warnings("${CMAKE_CURRENT_SOURCE_DIR}/deps_src")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Warning level for the targets added below: our sources, plus glad and libvgcode,
|
|
||||||
# which are vendored but live under src/. The deps_src libraries were configured just
|
|
||||||
# above. CMP0092 left MSVC without a default level, so it is set here.
|
|
||||||
if (NOT SLIC3R_WARNINGS)
|
|
||||||
add_compile_options(-w)
|
|
||||||
elseif (MSVC AND NOT IS_CLANG_CL)
|
|
||||||
# /we4715 is C4715, no return from a non-void function, an error on the GNU/Clang
|
|
||||||
# builds under -Werror. MSVC is not in that model, so this stays a single promoted
|
|
||||||
# warning.
|
|
||||||
add_compile_options(/W3 /we4715)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT OrcaSlicer_app_gui)
|
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT OrcaSlicer_app_gui)
|
||||||
|
|
||||||
@@ -1236,10 +885,6 @@ endif()
|
|||||||
|
|
||||||
if(BUILD_TESTS)
|
if(BUILD_TESTS)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
if (NOT SLIC3R_BUNDLED_WARNINGS)
|
|
||||||
# Catch2 is vendored under tests/ and sets its own warning flags too.
|
|
||||||
orcaslicer_silence_third_party_warnings("${CMAKE_CURRENT_SOURCE_DIR}/tests/catch2")
|
|
||||||
endif ()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT WIN32 AND NOT APPLE)
|
if (NOT WIN32 AND NOT APPLE)
|
||||||
@@ -1247,12 +892,10 @@ if (NOT WIN32 AND NOT APPLE)
|
|||||||
configure_file(${LIBDIR}/dev-utils/platform/unix/build_appimage.sh.in ${CMAKE_CURRENT_BINARY_DIR}/build_appimage.sh USE_SOURCE_PERMISSIONS @ONLY)
|
configure_file(${LIBDIR}/dev-utils/platform/unix/build_appimage.sh.in ${CMAKE_CURRENT_BINARY_DIR}/build_appimage.sh USE_SOURCE_PERMISSIONS @ONLY)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
# Resources install target, configure fhs.hpp on UNIX
|
# Resources install target, configure fhs.hpp on UNIX
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "./resources")
|
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "./resources")
|
||||||
if(ORCA_BUNDLED_UV_EXECUTABLE)
|
|
||||||
install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "./resources/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}")
|
|
||||||
endif()
|
|
||||||
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
|
||||||
include(InstallRequiredSystemLibraries)
|
include(InstallRequiredSystemLibraries)
|
||||||
install (PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ".")
|
install (PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ".")
|
||||||
@@ -1262,9 +905,6 @@ elseif (SLIC3R_FHS)
|
|||||||
install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/ DESTINATION ${SLIC3R_FHS_RESOURCES}
|
install(DIRECTORY ${SLIC3R_RESOURCES_DIR}/ DESTINATION ${SLIC3R_FHS_RESOURCES}
|
||||||
PATTERN "*/udev" EXCLUDE
|
PATTERN "*/udev" EXCLUDE
|
||||||
)
|
)
|
||||||
if(ORCA_BUNDLED_UV_EXECUTABLE)
|
|
||||||
install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "${SLIC3R_FHS_RESOURCES}/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}")
|
|
||||||
endif()
|
|
||||||
install(FILES src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
install(FILES src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications)
|
||||||
foreach(SIZE 32 128 192)
|
foreach(SIZE 32 128 192)
|
||||||
install(FILES ${SLIC3R_RESOURCES_DIR}/images/OrcaSlicer_${SIZE}px.png
|
install(FILES ${SLIC3R_RESOURCES_DIR}/images/OrcaSlicer_${SIZE}px.png
|
||||||
@@ -1273,29 +913,9 @@ elseif (SLIC3R_FHS)
|
|||||||
endforeach()
|
endforeach()
|
||||||
elseif (CMAKE_MACOSX_BUNDLE)
|
elseif (CMAKE_MACOSX_BUNDLE)
|
||||||
# install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/OrcaSlicer.app/Contents/resources")
|
# install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/OrcaSlicer.app/Contents/resources")
|
||||||
if(ORCA_BUNDLED_UV_EXECUTABLE)
|
|
||||||
install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "${CMAKE_INSTALL_PREFIX}/OrcaSlicer.app/Contents/Resources/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}")
|
|
||||||
endif()
|
|
||||||
else ()
|
else ()
|
||||||
install(FILES src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications)
|
install(FILES src/dev-utils/platform/unix/com.orcaslicer.OrcaSlicer.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/resources/applications)
|
||||||
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
|
install(DIRECTORY "${SLIC3R_RESOURCES_DIR}/" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources")
|
||||||
if(ORCA_BUNDLED_UV_EXECUTABLE)
|
|
||||||
install(PROGRAMS "${ORCA_BUNDLED_UV_EXECUTABLE}" DESTINATION "${CMAKE_INSTALL_PREFIX}/resources/tools/uv" RENAME "${ORCA_BUNDLED_UV_FILENAME}")
|
|
||||||
endif()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
|
||||||
set(LIBRARY_FILES
|
|
||||||
${LIBDIR_BIN}/libavcodec.so.61
|
|
||||||
${LIBDIR_BIN}/libavcodec.so.61.3.100
|
|
||||||
${LIBDIR_BIN}/libavutil.so.59
|
|
||||||
${LIBDIR_BIN}/libavutil.so.59.8.100
|
|
||||||
${LIBDIR_BIN}/libswresample.so.5
|
|
||||||
${LIBDIR_BIN}/libswresample.so.5.1.100
|
|
||||||
${LIBDIR_BIN}/libswscale.so.8
|
|
||||||
${LIBDIR_BIN}/libswscale.so.8.1.100
|
|
||||||
)
|
|
||||||
install(FILES ${LIBRARY_FILES} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.txt DESTINATION ".")
|
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.txt DESTINATION ".")
|
||||||
@@ -1307,16 +927,6 @@ set (CPACK_PACKAGE_VERSION_MAJOR "${ORCA_VERSION_MAJOR}")
|
|||||||
set (CPACK_PACKAGE_VERSION_MINOR "${ORCA_VERSION_MINOR}")
|
set (CPACK_PACKAGE_VERSION_MINOR "${ORCA_VERSION_MINOR}")
|
||||||
set (CPACK_PACKAGE_VERSION_PATCH "${ORCA_VERSION_PATCH}")
|
set (CPACK_PACKAGE_VERSION_PATCH "${ORCA_VERSION_PATCH}")
|
||||||
set (CPACK_PACKAGE_FILE_NAME "OrcaSlicer_Windows_Installer_V${SoftFever_VERSION}")
|
set (CPACK_PACKAGE_FILE_NAME "OrcaSlicer_Windows_Installer_V${SoftFever_VERSION}")
|
||||||
# Suffix the Windows installer with its target arch so the x64 and arm64 builds
|
|
||||||
# produce distinct filenames (matches ARCH_SUFFIX in build_orca.yml). Same
|
|
||||||
# CMAKE_SYSTEM_PROCESSOR mapping used by orcaslicer_copy_dlls() above.
|
|
||||||
if (WIN32)
|
|
||||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
|
|
||||||
string (APPEND CPACK_PACKAGE_FILE_NAME "_arm64")
|
|
||||||
else ()
|
|
||||||
string (APPEND CPACK_PACKAGE_FILE_NAME "_x64")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Orca Slicer is an open source slicer for FDM printers")
|
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Orca Slicer is an open source slicer for FDM printers")
|
||||||
set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/OrcaSlicer/OrcaSlicer")
|
set (CPACK_PACKAGE_HOMEPAGE_URL "https://github.com/OrcaSlicer/OrcaSlicer")
|
||||||
set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
|
set (CPACK_PACKAGE_INSTALL_DIRECTORY ${CPACK_PACKAGE_NAME})
|
||||||
@@ -1324,6 +934,9 @@ set (CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources/images\\\\OrcaSlicer.ico"
|
|||||||
set (CPACK_NSIS_MUI_ICON "${CPACK_PACKAGE_ICON}")
|
set (CPACK_NSIS_MUI_ICON "${CPACK_PACKAGE_ICON}")
|
||||||
set (CPACK_NSIS_MUI_UNIICON "${CPACK_PACKAGE_ICON}")
|
set (CPACK_NSIS_MUI_UNIICON "${CPACK_PACKAGE_ICON}")
|
||||||
set (CPACK_NSIS_INSTALLED_ICON_NAME "$INSTDIR\\\\orca-slicer.exe")
|
set (CPACK_NSIS_INSTALLED_ICON_NAME "$INSTDIR\\\\orca-slicer.exe")
|
||||||
|
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "
|
||||||
|
CreateShortCut \\\"$DESKTOP\\\\OrcaSlicer.lnk\\\" \\\"$INSTDIR\\\\orca-slicer.exe\\\"
|
||||||
|
")
|
||||||
set (CPACK_PACKAGE_CHECKSUM SHA256)
|
set (CPACK_PACKAGE_CHECKSUM SHA256)
|
||||||
set (CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OrcaSlicer")
|
set (CPACK_PACKAGE_INSTALL_REGISTRY_KEY "OrcaSlicer")
|
||||||
set (CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
|
set (CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
|
||||||
|
|||||||
@@ -10,24 +10,20 @@
|
|||||||
|
|
||||||
OrcaSlicer: an open source Next-Gen Slicing Software for Precision 3D Prints.
|
OrcaSlicer: an open source Next-Gen Slicing Software for Precision 3D Prints.
|
||||||
Optimize your prints with ultra-fast slicing, intelligent support generation, and seamless printer compatibility—engineered for perfection.
|
Optimize your prints with ultra-fast slicing, intelligent support generation, and seamless printer compatibility—engineered for perfection.
|
||||||
|
<h3>
|
||||||
|
|
||||||
# Official links and community
|
# Official links and community
|
||||||
|
|
||||||
#### Official Website:
|
#### Official Website:
|
||||||
|
|
||||||
<a href="https://www.orcaslicer.com/" style="font-size:2em;">OrcaSlicer.com</a>
|
<a href="https://www.orcaslicer.com/" style="font-size:2em;">OrcaSlicer.com</a>
|
||||||
|
|
||||||
#### Github Repository:
|
#### Github Repository:
|
||||||
|
|
||||||
<a href="https://github.com/OrcaSlicer/OrcaSlicer"><img src="https://img.shields.io/badge/OrcaSlicer-181717?style=flat&logo=github&logoColor=white" width="200" alt="GitHub Logo"/> </a>
|
<a href="https://github.com/OrcaSlicer/OrcaSlicer"><img src="https://img.shields.io/badge/OrcaSlicer-181717?style=flat&logo=github&logoColor=white" width="200" alt="GitHub Logo"/> </a>
|
||||||
|
|
||||||
#### Follow us:
|
#### Follow us:
|
||||||
|
|
||||||
<a href="https://twitter.com/real_OrcaSlicer"><img src="https://img.shields.io/badge/real__OrcaSlicer-000000?style=flat&logo=x&logoColor=white" width="200" alt="X Logo"/> </a>
|
<a href="https://twitter.com/real_OrcaSlicer"><img src="https://img.shields.io/badge/real__OrcaSlicer-000000?style=flat&logo=x&logoColor=white" width="200" alt="X Logo"/> </a>
|
||||||
<a href="https://www.youtube.com/@OfficialOrcaSlicer"><img src="https://img.shields.io/badge/OfficialOrcaSlicer-FF0000?style=flat&logo=youtube&logoColor=white" width="200" alt="YouTube Logo"/> </a>
|
|
||||||
|
|
||||||
#### Join our Discord community:
|
#### Join our Discord community:
|
||||||
|
|
||||||
<a href="https://discord.gg/P4VE9UY9gJ"><img src="https://img.shields.io/badge/-Discord-5865F2?style=flat&logo=discord&logoColor=fff" width="200" alt="discord logo"/> </a>
|
<a href="https://discord.gg/P4VE9UY9gJ"><img src="https://img.shields.io/badge/-Discord-5865F2?style=flat&logo=discord&logoColor=fff" width="200" alt="discord logo"/> </a>
|
||||||
|
|
||||||
<table border="2" style="border-color: #ffa500; background-color:rgb(232, 220, 180); color: #856404;">
|
<table border="2" style="border-color: #ffa500; background-color:rgb(232, 220, 180); color: #856404;">
|
||||||
@@ -89,41 +85,26 @@ Visit our GitHub Releases page for the latest stable version of OrcaSlicer, reco
|
|||||||
🌙 **[Download the Latest Nightly Build](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds)**
|
🌙 **[Download the Latest Nightly Build](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds)**
|
||||||
Explore the latest developments in OrcaSlicer with our nightly builds. Feedback on these versions is highly appreciated.
|
Explore the latest developments in OrcaSlicer with our nightly builds. Feedback on these versions is highly appreciated.
|
||||||
|
|
||||||
### Belt Printer Builds
|
|
||||||
|
|
||||||
The [nightly release](https://github.com/OrcaSlicer/OrcaSlicer/releases/tag/nightly-builds) ships **two parallel builds**: the standard build and a belt-printer build. Both are attached to the same release — tell them apart by the filename suffix:
|
|
||||||
|
|
||||||
- **Standard** — no suffix (e.g. `OrcaSlicer_Windows_Installer_x64_nightly.exe`)
|
|
||||||
- **Belt** — `_belt` suffix (e.g. `OrcaSlicer_Windows_Installer_x64_nightly_belt.exe`)
|
|
||||||
|
|
||||||
The `_belt` builds add **experimental support for belt / conveyor (infinite-Z) printers**, where the model is sliced against a tilted belt surface instead of a flat horizontal bed. They include ready-to-use belt printer profiles, the full belt slicing pipeline (mesh rotation and G-code transforms), belt-aware support generation, and a tilted-bed preview.
|
|
||||||
|
|
||||||
> ⚠️ Belt printer support is under active development and is **not yet merged into `main`** — it currently ships only in these parallel `_belt` builds, produced from the [`belt-printer`](https://github.com/OrcaSlicer/OrcaSlicer/tree/belt-printer) branch. See tracking PR [#14394](https://github.com/OrcaSlicer/OrcaSlicer/pull/14394) and the original documentation in [#12998](https://github.com/OrcaSlicer/OrcaSlicer/pull/12998).
|
|
||||||
|
|
||||||
# How to install
|
# How to install
|
||||||
|
|
||||||
## Windows
|
## Windows
|
||||||
|
|
||||||
Download the **Windows Installer exe** for your preferred version from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases). Both `x64` and `arm64` installers are published — pick the one matching your CPU.
|
Download the **Windows Installer exe** for your preferred version from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
||||||
|
|
||||||
- *For convenience there is also a portable build available.*
|
- *For convenience there is also a portable build available.*
|
||||||
<details>
|
<details>
|
||||||
<summary>Troubleshooting</summary>
|
<summary>Troubleshooting</summary>
|
||||||
|
|
||||||
- *If you have troubles to run the build, you might need to install following runtimes:*
|
- *If you have troubles to run the build, you might need to install following runtimes:*
|
||||||
- [MicrosoftEdgeWebView2RuntimeInstallerX64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/MicrosoftEdgeWebView2RuntimeInstallerX64.exe)
|
- [MicrosoftEdgeWebView2RuntimeInstallerX64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/MicrosoftEdgeWebView2RuntimeInstallerX64.exe)
|
||||||
- [Details of this runtime](https://aka.ms/webview2)
|
- [Details of this runtime](https://aka.ms/webview2)
|
||||||
- [Alternative Download Link Hosted by Microsoft](https://go.microsoft.com/fwlink/p/?LinkId=2124703)
|
- [Alternative Download Link Hosted by Microsoft](https://go.microsoft.com/fwlink/p/?LinkId=2124703)
|
||||||
- [vcredist2019_x64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/vcredist2019_x64.exe)
|
- [vcredist2019_x64](https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v1.0.10-sf2/vcredist2019_x64.exe)
|
||||||
- [Alternative Download Link Hosted by Microsoft](https://aka.ms/vs/17/release/vc_redist.x64.exe)
|
- [Alternative Download Link Hosted by Microsoft](https://aka.ms/vs/17/release/vc_redist.x64.exe)
|
||||||
- This file may already be available on your computer if you've installed visual studio. Check the following location: `%VCINSTALLDIR%Redist\MSVC\v142`
|
- This file may already be available on your computer if you've installed visual studio. Check the following location: `%VCINSTALLDIR%Redist\MSVC\v142`
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Microsoft Store
|
Windows Package Manager
|
||||||
|
|
||||||
Install from the [Microsoft Store](https://apps.microsoft.com/detail/9mv6gl23xm59) when you prefer a Store-signed package (helps on Windows 11 Smart App Control).
|
|
||||||
|
|
||||||
### Windows Package Manager
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
winget install --id=SoftFever.OrcaSlicer -e
|
winget install --id=SoftFever.OrcaSlicer -e
|
||||||
@@ -131,7 +112,7 @@ winget install --id=SoftFever.OrcaSlicer -e
|
|||||||
|
|
||||||
## Mac
|
## Mac
|
||||||
|
|
||||||
1. Download the universal DMG, which runs on both Apple Silicon and Intel Macs.
|
1. Download the DMG for your computer: `arm64` version for Apple Silicon and `x86_64` for Intel CPU.
|
||||||
2. Drag OrcaSlicer.app to Application folder.
|
2. Drag OrcaSlicer.app to Application folder.
|
||||||
3. *If you want to run a build from a PR, you also need to follow the instructions below:*
|
3. *If you want to run a build from a PR, you also need to follow the instructions below:*
|
||||||
|
|
||||||
@@ -156,18 +137,9 @@ winget install --id=SoftFever.OrcaSlicer -e
|
|||||||

|

|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
### Homebrew Cask
|
|
||||||
|
|
||||||
```shell
|
|
||||||
brew install --cask orcaslicer
|
|
||||||
```
|
|
||||||
|
|
||||||
The [Homebrew cask](https://formulae.brew.sh/cask/orcaslicer) installs the official macOS DMG from [GitHub Releases](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
|
||||||
|
|
||||||
## Linux
|
## Linux
|
||||||
|
|
||||||
### Flathub (Recommended)
|
### Flathub (Recommended)
|
||||||
|
|
||||||
OrcaSlicer is available through FlatHub:
|
OrcaSlicer is available through FlatHub:
|
||||||
|
|
||||||
<a href='https://flathub.org/apps/com.orcaslicer.OrcaSlicer'><img width='240' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
<a href='https://flathub.org/apps/com.orcaslicer.OrcaSlicer'><img width='240' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.png'/></a>
|
||||||
@@ -182,9 +154,6 @@ flatpak run com.orcaslicer.OrcaSlicer
|
|||||||
It can also be installed through graphical software managers (KDE Discover, GNOME Software, etc.) when Flathub is enabled. Search for **OrcaSlicer** in your software center.
|
It can also be installed through graphical software managers (KDE Discover, GNOME Software, etc.) when Flathub is enabled. Search for **OrcaSlicer** in your software center.
|
||||||
|
|
||||||
### AppImage
|
### AppImage
|
||||||
|
|
||||||
AppImages are published for both **x86_64** and **aarch64** (ARM64). Pick the file matching your CPU — the ARM64 build has `aarch64` in its name (e.g. `OrcaSlicer_Linux_AppImage_Ubuntu2404_aarch64_*.AppImage`).
|
|
||||||
|
|
||||||
1. Download App image from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
1. Download App image from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
||||||
2. Double click the downloaded file to run it.
|
2. Double click the downloaded file to run it.
|
||||||
|
|
||||||
@@ -212,11 +181,11 @@ resolution: 0.1
|
|||||||
|
|
||||||
# Supports
|
# Supports
|
||||||
|
|
||||||
**OrcaSlicer** is an open-source project, and we're deeply grateful to all our sponsors and backers.
|
**OrcaSlicer** is an open-source project and I'm deeply grateful to all my sponsors and backers.
|
||||||
Their generous support helps fund filaments and other essential 3D printing materials for the project.
|
Their generous support enables me to purchase filaments and other essential 3D printing materials for the project.
|
||||||
Thank you! :)
|
Thank you! :)
|
||||||
|
|
||||||
## Sponsors
|
## Sponsors:
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
@@ -237,7 +206,7 @@ Thank you! :)
|
|||||||
|
|
||||||
**Ko-fi supporters** ☕: [Backers list](https://github.com/user-attachments/files/16147016/Supporters_638561417699952499.csv)
|
**Ko-fi supporters** ☕: [Backers list](https://github.com/user-attachments/files/16147016/Supporters_638561417699952499.csv)
|
||||||
|
|
||||||
## Support the project
|
## Support me
|
||||||
|
|
||||||
<a href="https://github.com/sponsors/SoftFever"><img src="https://img.shields.io/badge/GitHub%20Sponsors-30363D?style=flat&logo=GitHub-Sponsors&logoColor=EA4AAA" height="50"></a>
|
<a href="https://github.com/sponsors/SoftFever"><img src="https://img.shields.io/badge/GitHub%20Sponsors-30363D?style=flat&logo=GitHub-Sponsors&logoColor=EA4AAA" height="50"></a>
|
||||||
<a href="https://ko-fi.com/G2G5IP3CP"><img src="https://img.shields.io/badge/Support_me_on_Ko--fi-FF5E5B?style=flat&logo=ko-fi&logoColor=white" height="50"></a>
|
<a href="https://ko-fi.com/G2G5IP3CP"><img src="https://img.shields.io/badge/Support_me_on_Ko--fi-FF5E5B?style=flat&logo=ko-fi&logoColor=white" height="50"></a>
|
||||||
@@ -252,7 +221,6 @@ OrcaSlicer began in that same spirit, drawing from BambuStudio, PrusaSlicer, and
|
|||||||
The OrcaSlicer logo was designed by community member [Justin Levine](https://github.com/jal-co).
|
The OrcaSlicer logo was designed by community member [Justin Levine](https://github.com/jal-co).
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
- **OrcaSlicer** is licensed under the GNU Affero General Public License, version 3.
|
- **OrcaSlicer** is licensed under the GNU Affero General Public License, version 3.
|
||||||
- The **GNU Affero General Public License**, version 3 ensures that if you use any part of this software in any way (even behind a web server), your software must be released under the same license.
|
- The **GNU Affero General Public License**, version 3 ensures that if you use any part of this software in any way (even behind a web server), your software must be released under the same license.
|
||||||
- OrcaSlicer includes a **pressure advance calibration pattern test** adapted from Andrew Ellis' generator, which is licensed under GNU General Public License, version 3. Ellis' generator is itself adapted from a generator developed by Sineos for Marlin, which is licensed under GNU General Public License, version 3.
|
- OrcaSlicer includes a **pressure advance calibration pattern test** adapted from Andrew Ellis' generator, which is licensed under GNU General Public License, version 3. Ellis' generator is itself adapted from a generator developed by Sineos for Marlin, which is licensed under GNU General Public License, version 3.
|
||||||
|
|||||||
@@ -260,9 +260,6 @@ if [[ ! -f "./scripts/flatpak/com.orcaslicer.OrcaSlicer.yml" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "${YELLOW}Packing deps/ for the manifest...${NC}"
|
|
||||||
./scripts/flatpak/make_deps_tar.sh
|
|
||||||
|
|
||||||
# Build the Flatpak
|
# Build the Flatpak
|
||||||
echo -e "${YELLOW}Building Flatpak package...${NC}"
|
echo -e "${YELLOW}Building Flatpak package...${NC}"
|
||||||
echo -e "This may take a while (30+ minutes depending on your system)..."
|
echo -e "This may take a while (30+ minutes depending on your system)..."
|
||||||
|
|||||||
+6
-19
@@ -504,24 +504,13 @@ if [[ -n "${USE_LLD}" ]] ; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Auto-detect ccache for faster rebuilds
|
||||||
export CMAKE_CCACHE_ARGS=()
|
export CMAKE_CCACHE_ARGS=()
|
||||||
CMAKE_CCACHE=${CMAKE_CCACHE:-}
|
if command -v ccache >/dev/null 2>&1 ; then
|
||||||
if [ -n "$CMAKE_CCACHE" ]; then
|
echo "ccache found at $(command -v ccache), enabling compiler caching..."
|
||||||
echo "Checking ${CMAKE_CCACHE} environment variable for compiler cache program..."
|
export CMAKE_CCACHE_ARGS=(-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache)
|
||||||
CMAKE_CCACHE=$(command -v "${CMAKE_CCACHE}") || {
|
|
||||||
echo "CMAKE_CCACHE environment variable is set to '${CMAKE_CCACHE}' but it was not found in PATH."
|
|
||||||
CMAKE_CCACHE=""
|
|
||||||
}
|
|
||||||
elif command -v sccache >/dev/null 2>&1 ; then
|
|
||||||
CMAKE_CCACHE=$(command -v sccache)
|
|
||||||
elif command -v ccache >/dev/null 2>&1 ; then
|
|
||||||
CMAKE_CCACHE=$(command -v ccache)
|
|
||||||
fi
|
|
||||||
if [ -n "${CMAKE_CCACHE}" ] ; then
|
|
||||||
echo "${CMAKE_CCACHE} found, enabling compiler caching..."
|
|
||||||
export CMAKE_CCACHE_ARGS=(-DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_CCACHE}" -DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CCACHE}")
|
|
||||||
else
|
else
|
||||||
echo "Note: ccache or sccache are not found. Install either of them for faster rebuilds."
|
echo "Note: ccache not found. Install ccache for faster rebuilds."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${BUILD_DEPS}" ]] ; then
|
if [[ -n "${BUILD_DEPS}" ]] ; then
|
||||||
@@ -536,7 +525,7 @@ if [[ -n "${BUILD_DEPS}" ]] ; then
|
|||||||
BUILD_ARGS+=(-DCMAKE_BUILD_TYPE="${BUILD_CONFIG}")
|
BUILD_ARGS+=(-DCMAKE_BUILD_TYPE="${BUILD_CONFIG}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" "${CMAKE_CCACHE_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
|
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
|
||||||
print_and_run cmake --build deps/$BUILD_DIR -j1
|
print_and_run cmake --build deps/$BUILD_DIR -j1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -567,8 +556,6 @@ if [[ -n "${BUILD_ORCA}" ]] || [[ -n "${BUILD_TESTS}" ]] ; then
|
|||||||
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer
|
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer
|
||||||
echo "Building OrcaSlicer_profile_validator .."
|
echo "Building OrcaSlicer_profile_validator .."
|
||||||
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer_profile_validator
|
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target OrcaSlicer_profile_validator
|
||||||
echo "Building generate_system_cache ..."
|
|
||||||
print_and_run cmake --build $BUILD_DIR --config "${BUILD_CONFIG}" --target generate_system_cache
|
|
||||||
./scripts/run_gettext.sh
|
./scripts/run_gettext.sh
|
||||||
fi
|
fi
|
||||||
if [[ -n "${BUILD_TESTS}" ]] ; then
|
if [[ -n "${BUILD_TESTS}" ]] ; then
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
set WP=%CD%
|
||||||
|
|
||||||
|
set debug=OFF
|
||||||
|
set debuginfo=OFF
|
||||||
|
if "%1"=="debug" set debug=ON
|
||||||
|
if "%2"=="debug" set debug=ON
|
||||||
|
if "%1"=="debuginfo" set debuginfo=ON
|
||||||
|
if "%2"=="debuginfo" set debuginfo=ON
|
||||||
|
if "%debug%"=="ON" (
|
||||||
|
set build_type=Debug
|
||||||
|
set build_dir=build-dbg
|
||||||
|
) else (
|
||||||
|
if "%debuginfo%"=="ON" (
|
||||||
|
set build_type=RelWithDebInfo
|
||||||
|
set build_dir=build-dbginfo
|
||||||
|
) else (
|
||||||
|
set build_type=Release
|
||||||
|
set build_dir=build
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo build type set to %build_type%
|
||||||
|
|
||||||
|
cd deps
|
||||||
|
mkdir %build_dir%
|
||||||
|
cd %build_dir%
|
||||||
|
set DEPS=%CD%/OrcaSlicer_dep
|
||||||
|
set "SIG_FLAG="
|
||||||
|
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
|
||||||
|
if "%1"=="slicer" (
|
||||||
|
GOTO :slicer
|
||||||
|
)
|
||||||
|
echo "building deps.."
|
||||||
|
|
||||||
|
echo cmake ../ -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake ../ -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake --build . --config %build_type% --target deps -- -m
|
||||||
|
|
||||||
|
if "%1"=="deps" exit /b 0
|
||||||
|
|
||||||
|
:slicer
|
||||||
|
echo "building Orca Slicer..."
|
||||||
|
cd %WP%
|
||||||
|
mkdir %build_dir%
|
||||||
|
cd %build_dir%
|
||||||
|
|
||||||
|
echo cmake .. -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake .. -G "Visual Studio 16 2019" -A x64 -DCMAKE_BUILD_TYPE=%build_type% %SIG_FLAG%
|
||||||
|
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
||||||
|
cd ..
|
||||||
|
call scripts/run_gettext.bat
|
||||||
|
cd %build_dir%
|
||||||
|
cmake --build . --target install --config %build_type%
|
||||||
+12
-74
@@ -4,7 +4,7 @@ set -e
|
|||||||
set -o pipefail
|
set -o pipefail
|
||||||
SECONDS=0
|
SECONDS=0
|
||||||
|
|
||||||
while getopts ":dpa:snt:xbc:i:j:Tuh" opt; do
|
while getopts ":dpa:snt:xbc:i:1Tuh" opt; do
|
||||||
case "${opt}" in
|
case "${opt}" in
|
||||||
d )
|
d )
|
||||||
export BUILD_TARGET="deps"
|
export BUILD_TARGET="deps"
|
||||||
@@ -38,8 +38,8 @@ while getopts ":dpa:snt:xbc:i:j:Tuh" opt; do
|
|||||||
i )
|
i )
|
||||||
export CMAKE_IGNORE_PREFIX_PATH="${CMAKE_IGNORE_PREFIX_PATH:+$CMAKE_IGNORE_PREFIX_PATH;}$OPTARG"
|
export CMAKE_IGNORE_PREFIX_PATH="${CMAKE_IGNORE_PREFIX_PATH:+$CMAKE_IGNORE_PREFIX_PATH;}$OPTARG"
|
||||||
;;
|
;;
|
||||||
j )
|
1 )
|
||||||
export CMAKE_BUILD_PARALLEL_LEVEL="$OPTARG"
|
export CMAKE_BUILD_PARALLEL_LEVEL=1
|
||||||
;;
|
;;
|
||||||
T )
|
T )
|
||||||
export BUILD_TESTS="1"
|
export BUILD_TESTS="1"
|
||||||
@@ -58,8 +58,8 @@ while getopts ":dpa:snt:xbc:i:j:Tuh" opt; do
|
|||||||
echo " -b: Build without reconfiguring CMake"
|
echo " -b: Build without reconfiguring CMake"
|
||||||
echo " -c: Set CMake build configuration, default is Release"
|
echo " -c: Set CMake build configuration, default is Release"
|
||||||
echo " -i: Add a prefix to ignore during CMake dependency discovery (repeatable), defaults to /opt/local:/usr/local:/opt/homebrew"
|
echo " -i: Add a prefix to ignore during CMake dependency discovery (repeatable), defaults to /opt/local:/usr/local:/opt/homebrew"
|
||||||
echo " -j: Set the number of parallel build jobs (CMAKE_BUILD_PARALLEL_LEVEL)"
|
echo " -1: Use single job for building"
|
||||||
echo " -T: Build and run tests (set ORCA_TESTS_BUILD_ONLY=1 to build without running)"
|
echo " -T: Build and run tests"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
* )
|
* )
|
||||||
@@ -179,66 +179,6 @@ function pack_deps() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
# codesign cannot seal the runtime's dotted directories (include/python3.12,
|
|
||||||
# lib/python3.12) anywhere under Contents/MacOS -- it mistakes any dotted
|
|
||||||
# directory there for a nested bundle and fails with "bundle format
|
|
||||||
# unrecognized" -- so packaged apps ship the runtime under Contents/Resources
|
|
||||||
# with a compatibility symlink that keeps every Contents/MacOS/python path and
|
|
||||||
# the @executable_path/python/lib rpath resolving unchanged.
|
|
||||||
function relocate_python_runtime() {
|
|
||||||
local app="$1"
|
|
||||||
local pydir="$app/Contents/MacOS/python"
|
|
||||||
if [ -d "$pydir" ] && [ ! -L "$pydir" ]; then
|
|
||||||
rm -rf "$app/Contents/Resources/python"
|
|
||||||
mv "$pydir" "$app/Contents/Resources/python"
|
|
||||||
ln -s ../Resources/python "$pydir"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Bundled Python runtime verification --------------------------------------
|
|
||||||
# Relocation is handled at the source: deps/python3/python3.cmake stamps
|
|
||||||
# libpython with an @rpath id and src/CMakeLists.txt gives the app a matching
|
|
||||||
# rpath. This gate catches regressions that would otherwise only surface as
|
|
||||||
# launch failures on end users' machines (the absolute deps path still exists
|
|
||||||
# on the build host, so a plain run can pass while relocation is broken --
|
|
||||||
# hence the otool checks). The x86_64 leg runs under Rosetta on arm64 hosts.
|
|
||||||
function verify_python_runtime() {
|
|
||||||
local app="$1"
|
|
||||||
local pydir="$app/Contents/MacOS/python"
|
|
||||||
[ -d "$pydir" ] || return 0 # app doesn't bundle Python (e.g. profile validator)
|
|
||||||
if [ ! -L "$pydir" ]; then
|
|
||||||
echo "ERROR: Contents/MacOS/python must be a symlink into Contents/Resources" >&2
|
|
||||||
echo " (see relocate_python_runtime in this script)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# Version-agnostic interpreter name so a CPython version bump cannot
|
|
||||||
# silently skip the gate; if the dir exists the interpreter must too.
|
|
||||||
local pybin="$pydir/bin/python3"
|
|
||||||
if [ ! -x "$pybin" ]; then
|
|
||||||
echo "ERROR: bundled python/ present but no interpreter at $pybin" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo " Verifying bundled Python runtime in $(basename "$app")..."
|
|
||||||
local bad
|
|
||||||
bad=$(otool -arch all -L "$pybin" "$app/Contents/MacOS/OrcaSlicer" | grep "libpython" | grep -v "@rpath/" || true)
|
|
||||||
if [ -n "$bad" ]; then
|
|
||||||
echo "ERROR: a bundled binary references libpython by absolute path (relocation regression):" >&2
|
|
||||||
echo "$bad" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# otool -L shows load commands only; assert the consumer rpath separately.
|
|
||||||
# Its loss is masked on the build host by CMake's absolute build-tree rpath.
|
|
||||||
if ! otool -arch all -l "$app/Contents/MacOS/OrcaSlicer" | grep -q "path @executable_path/python/lib "; then
|
|
||||||
echo "ERROR: OrcaSlicer lacks the @executable_path/python/lib rpath (relocation regression)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if ! "$pybin" -c "import ssl"; then
|
|
||||||
echo "ERROR: bundled Python failed to start (libpython relocation broken," >&2
|
|
||||||
echo " or missing Rosetta 2 for the x86_64 leg?)" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function build_slicer() {
|
function build_slicer() {
|
||||||
# iterate over two architectures: x86_64 and arm64
|
# iterate over two architectures: x86_64 and arm64
|
||||||
for _ARCH in x86_64 arm64; do
|
for _ARCH in x86_64 arm64; do
|
||||||
@@ -269,10 +209,13 @@ function build_slicer() {
|
|||||||
cmake --build . --config "$BUILD_CONFIG" --target "$SLICER_BUILD_TARGET"
|
cmake --build . --config "$BUILD_CONFIG" --target "$SLICER_BUILD_TARGET"
|
||||||
)
|
)
|
||||||
|
|
||||||
# -T also runs the tests; ORCA_TESTS_BUILD_ONLY=1 builds them without
|
if [ "1." == "$BUILD_TESTS". ]; then
|
||||||
# running, so CI can build here and run them in a dedicated job.
|
echo "Running tests for $_ARCH..."
|
||||||
if [ "1." == "$BUILD_TESTS". ] && [ "1." != "$ORCA_TESTS_BUILD_ONLY". ]; then
|
(
|
||||||
"$PROJECT_DIR/scripts/run_unit_tests.sh" "build/$_ARCH/tests" "$BUILD_CONFIG"
|
set -x
|
||||||
|
cd "$PROJECT_BUILD_DIR"
|
||||||
|
ctest --build-config "$BUILD_CONFIG" --output-on-failure
|
||||||
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Verify localization with gettext..."
|
echo "Verify localization with gettext..."
|
||||||
@@ -294,12 +237,9 @@ function build_slicer() {
|
|||||||
resources_path=$(readlink ./OrcaSlicer.app/Contents/Resources)
|
resources_path=$(readlink ./OrcaSlicer.app/Contents/Resources)
|
||||||
rm ./OrcaSlicer.app/Contents/Resources
|
rm ./OrcaSlicer.app/Contents/Resources
|
||||||
cp -R "$resources_path" ./OrcaSlicer.app/Contents/Resources
|
cp -R "$resources_path" ./OrcaSlicer.app/Contents/Resources
|
||||||
relocate_python_runtime ./OrcaSlicer.app
|
|
||||||
# delete .DS_Store file
|
# delete .DS_Store file
|
||||||
find ./OrcaSlicer.app/ -name '.DS_Store' -delete
|
find ./OrcaSlicer.app/ -name '.DS_Store' -delete
|
||||||
|
|
||||||
verify_python_runtime ./OrcaSlicer.app
|
|
||||||
|
|
||||||
# Copy OrcaSlicer_profile_validator.app if it exists
|
# Copy OrcaSlicer_profile_validator.app if it exists
|
||||||
if [ -f "../src$BUILD_DIR_CONFIG_SUBDIR/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
if [ -f "../src$BUILD_DIR_CONFIG_SUBDIR/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
||||||
echo "Copying OrcaSlicer_profile_validator.app..."
|
echo "Copying OrcaSlicer_profile_validator.app..."
|
||||||
@@ -307,7 +247,6 @@ function build_slicer() {
|
|||||||
cp -pR "../src$BUILD_DIR_CONFIG_SUBDIR/OrcaSlicer_profile_validator.app" ./OrcaSlicer_profile_validator.app
|
cp -pR "../src$BUILD_DIR_CONFIG_SUBDIR/OrcaSlicer_profile_validator.app" ./OrcaSlicer_profile_validator.app
|
||||||
# delete .DS_Store file
|
# delete .DS_Store file
|
||||||
find ./OrcaSlicer_profile_validator.app/ -name '.DS_Store' -delete
|
find ./OrcaSlicer_profile_validator.app/ -name '.DS_Store' -delete
|
||||||
verify_python_runtime ./OrcaSlicer_profile_validator.app
|
|
||||||
fi
|
fi
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -363,7 +302,6 @@ function build_universal() {
|
|||||||
echo "Creating universal binaries for OrcaSlicer.app..."
|
echo "Creating universal binaries for OrcaSlicer.app..."
|
||||||
lipo_dir "$UNIVERSAL_APP" "$X86_64_APP"
|
lipo_dir "$UNIVERSAL_APP" "$X86_64_APP"
|
||||||
echo "Universal OrcaSlicer.app created at $UNIVERSAL_APP"
|
echo "Universal OrcaSlicer.app created at $UNIVERSAL_APP"
|
||||||
verify_python_runtime "$UNIVERSAL_APP"
|
|
||||||
|
|
||||||
# Create universal binary for profile validator if it exists
|
# Create universal binary for profile validator if it exists
|
||||||
ARM64_VALIDATOR="$PROJECT_DIR/build/arm64/OrcaSlicer/OrcaSlicer_profile_validator.app"
|
ARM64_VALIDATOR="$PROJECT_DIR/build/arm64/OrcaSlicer/OrcaSlicer_profile_validator.app"
|
||||||
|
|||||||
@@ -0,0 +1,158 @@
|
|||||||
|
@REM OrcaSlicer build script for Windows with VS auto-detect
|
||||||
|
@echo off
|
||||||
|
set WP=%CD%
|
||||||
|
set _START_TIME=%TIME%
|
||||||
|
|
||||||
|
@REM Check for Ninja Multi-Config option (-x)
|
||||||
|
set USE_NINJA=0
|
||||||
|
for %%a in (%*) do (
|
||||||
|
if "%%a"=="-x" set USE_NINJA=1
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%USE_NINJA%"=="1" (
|
||||||
|
echo Using Ninja Multi-Config generator
|
||||||
|
set CMAKE_GENERATOR="Ninja Multi-Config"
|
||||||
|
set VS_VERSION=Ninja
|
||||||
|
goto :generator_ready
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Detect Visual Studio version using msbuild
|
||||||
|
echo Detecting Visual Studio version using msbuild...
|
||||||
|
|
||||||
|
@REM Try to get MSBuild version - the output format varies by VS version
|
||||||
|
set VS_MAJOR=
|
||||||
|
for /f "tokens=*" %%i in ('msbuild -version 2^>^&1 ^| findstr /r "^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"') do (
|
||||||
|
for /f "tokens=1 delims=." %%a in ("%%i") do set VS_MAJOR=%%a
|
||||||
|
set MSBUILD_OUTPUT=%%i
|
||||||
|
goto :version_found
|
||||||
|
)
|
||||||
|
|
||||||
|
@REM Alternative method for newer MSBuild versions
|
||||||
|
if "%VS_MAJOR%"=="" (
|
||||||
|
for /f "tokens=*" %%i in ('msbuild -version 2^>^&1 ^| findstr /r "[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*"') do (
|
||||||
|
for /f "tokens=1 delims=." %%a in ("%%i") do set VS_MAJOR=%%a
|
||||||
|
set MSBUILD_OUTPUT=%%i
|
||||||
|
goto :version_found
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
:version_found
|
||||||
|
echo MSBuild version detected: %MSBUILD_OUTPUT%
|
||||||
|
echo Major version: %VS_MAJOR%
|
||||||
|
|
||||||
|
if "%VS_MAJOR%"=="" (
|
||||||
|
echo Error: Could not determine Visual Studio version from msbuild
|
||||||
|
echo Please ensure Visual Studio and MSBuild are properly installed
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
if "%VS_MAJOR%"=="16" (
|
||||||
|
set VS_VERSION=2019
|
||||||
|
set CMAKE_GENERATOR="Visual Studio 16 2019"
|
||||||
|
) else if "%VS_MAJOR%"=="17" (
|
||||||
|
set VS_VERSION=2022
|
||||||
|
set CMAKE_GENERATOR="Visual Studio 17 2022"
|
||||||
|
) else if "%VS_MAJOR%"=="18" (
|
||||||
|
set VS_VERSION=2026
|
||||||
|
set CMAKE_GENERATOR="Visual Studio 18 2026"
|
||||||
|
) else (
|
||||||
|
echo Error: Unsupported Visual Studio version: %VS_MAJOR%
|
||||||
|
echo Supported versions: VS2019 (16.x^), VS2022 (17.x^), VS2026 (18.x^)
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Detected Visual Studio %VS_VERSION% (version %VS_MAJOR%)
|
||||||
|
echo Using CMake generator: %CMAKE_GENERATOR%
|
||||||
|
|
||||||
|
:generator_ready
|
||||||
|
|
||||||
|
@REM Pack deps
|
||||||
|
if "%1"=="pack" (
|
||||||
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
cd %WP%/deps/build
|
||||||
|
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do set build_date=%%c%%b%%a
|
||||||
|
echo packing deps: OrcaSlicer_dep_win64_!build_date!_vs!VS_VERSION!.zip
|
||||||
|
|
||||||
|
%WP%/tools/7z.exe a OrcaSlicer_dep_win64_!build_date!_vs!VS_VERSION!.zip OrcaSlicer_dep
|
||||||
|
goto :done
|
||||||
|
)
|
||||||
|
|
||||||
|
set debug=OFF
|
||||||
|
set debuginfo=OFF
|
||||||
|
if "%1"=="debug" set debug=ON
|
||||||
|
if "%2"=="debug" set debug=ON
|
||||||
|
if "%1"=="debuginfo" set debuginfo=ON
|
||||||
|
if "%2"=="debuginfo" set debuginfo=ON
|
||||||
|
if "%debug%"=="ON" (
|
||||||
|
set build_type=Debug
|
||||||
|
set build_dir=build-dbg
|
||||||
|
) else (
|
||||||
|
if "%debuginfo%"=="ON" (
|
||||||
|
set build_type=RelWithDebInfo
|
||||||
|
set build_dir=build-dbginfo
|
||||||
|
) else (
|
||||||
|
set build_type=Release
|
||||||
|
set build_dir=build
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo build type set to %build_type%
|
||||||
|
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
cd deps
|
||||||
|
mkdir %build_dir%
|
||||||
|
cd %build_dir%
|
||||||
|
set "SIG_FLAG="
|
||||||
|
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
|
||||||
|
|
||||||
|
if "%1"=="slicer" (
|
||||||
|
GOTO :slicer
|
||||||
|
)
|
||||||
|
echo "building deps.."
|
||||||
|
|
||||||
|
echo on
|
||||||
|
REM Set minimum CMake policy to avoid <3.5 errors
|
||||||
|
set CMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
|
if "%USE_NINJA%"=="1" (
|
||||||
|
cmake ../ -G %CMAKE_GENERATOR% -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake --build . --config %build_type% --target deps
|
||||||
|
) else (
|
||||||
|
cmake ../ -G %CMAKE_GENERATOR% -A x64 -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake --build . --config %build_type% --target deps -- -m
|
||||||
|
)
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
if "%1"=="deps" goto :done
|
||||||
|
|
||||||
|
:slicer
|
||||||
|
echo "building Orca Slicer..."
|
||||||
|
cd %WP%
|
||||||
|
mkdir %build_dir%
|
||||||
|
cd %build_dir%
|
||||||
|
|
||||||
|
echo on
|
||||||
|
set CMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
|
if "%USE_NINJA%"=="1" (
|
||||||
|
cmake .. -G %CMAKE_GENERATOR% -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake --build . --config %build_type% --target ALL_BUILD
|
||||||
|
) else (
|
||||||
|
cmake .. -G %CMAKE_GENERATOR% -A x64 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
||||||
|
)
|
||||||
|
@echo off
|
||||||
|
cd ..
|
||||||
|
call scripts/run_gettext.bat
|
||||||
|
cd %build_dir%
|
||||||
|
cmake --build . --target install --config %build_type%
|
||||||
|
|
||||||
|
:done
|
||||||
|
@echo off
|
||||||
|
for /f "tokens=1-3 delims=:.," %%a in ("%_START_TIME: =0%") do set /a "_start_s=%%a*3600+%%b*60+%%c"
|
||||||
|
for /f "tokens=1-3 delims=:.," %%a in ("%TIME: =0%") do set /a "_end_s=%%a*3600+%%b*60+%%c"
|
||||||
|
set /a "_elapsed=_end_s - _start_s"
|
||||||
|
if %_elapsed% lss 0 set /a "_elapsed+=86400"
|
||||||
|
set /a "_hours=_elapsed / 3600"
|
||||||
|
set /a "_remainder=_elapsed - _hours * 3600"
|
||||||
|
set /a "_mins=_remainder / 60"
|
||||||
|
set /a "_secs=_remainder - _mins * 60"
|
||||||
|
echo.
|
||||||
|
echo Build completed in %_hours%h %_mins%m %_secs%s
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
@REM OrcaSlicer build script for Windows
|
||||||
|
@echo off
|
||||||
|
set WP=%CD%
|
||||||
|
|
||||||
|
@REM Pack deps
|
||||||
|
if "%1"=="pack" (
|
||||||
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
cd %WP%/deps/build
|
||||||
|
for /f "tokens=2-4 delims=/ " %%a in ('date /t') do set build_date=%%c%%b%%a
|
||||||
|
echo packing deps: OrcaSlicer_dep_win64_!build_date!_vs2022.zip
|
||||||
|
|
||||||
|
%WP%/tools/7z.exe a OrcaSlicer_dep_win64_!build_date!_vs2022.zip OrcaSlicer_dep
|
||||||
|
exit /b 0
|
||||||
|
)
|
||||||
|
|
||||||
|
set debug=OFF
|
||||||
|
set debuginfo=OFF
|
||||||
|
if "%1"=="debug" set debug=ON
|
||||||
|
if "%2"=="debug" set debug=ON
|
||||||
|
if "%1"=="debuginfo" set debuginfo=ON
|
||||||
|
if "%2"=="debuginfo" set debuginfo=ON
|
||||||
|
if "%debug%"=="ON" (
|
||||||
|
set build_type=Debug
|
||||||
|
set build_dir=build-dbg
|
||||||
|
) else (
|
||||||
|
if "%debuginfo%"=="ON" (
|
||||||
|
set build_type=RelWithDebInfo
|
||||||
|
set build_dir=build-dbginfo
|
||||||
|
) else (
|
||||||
|
set build_type=Release
|
||||||
|
set build_dir=build
|
||||||
|
)
|
||||||
|
)
|
||||||
|
echo build type set to %build_type%
|
||||||
|
|
||||||
|
setlocal DISABLEDELAYEDEXPANSION
|
||||||
|
cd deps
|
||||||
|
mkdir %build_dir%
|
||||||
|
cd %build_dir%
|
||||||
|
set "SIG_FLAG="
|
||||||
|
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
|
||||||
|
|
||||||
|
if "%1"=="slicer" (
|
||||||
|
GOTO :slicer
|
||||||
|
)
|
||||||
|
echo "building deps.."
|
||||||
|
|
||||||
|
echo on
|
||||||
|
REM Set minimum CMake policy to avoid <3.5 errors
|
||||||
|
set CMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
|
cmake ../ -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake --build . --config %build_type% --target deps -- -m
|
||||||
|
@echo off
|
||||||
|
|
||||||
|
if "%1"=="deps" exit /b 0
|
||||||
|
|
||||||
|
:slicer
|
||||||
|
echo "building Orca Slicer..."
|
||||||
|
cd %WP%
|
||||||
|
mkdir %build_dir%
|
||||||
|
cd %build_dir%
|
||||||
|
|
||||||
|
echo on
|
||||||
|
set CMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||||
|
cmake .. -G "Visual Studio 17 2022" -A x64 -DORCA_TOOLS=ON %SIG_FLAG% -DCMAKE_BUILD_TYPE=%build_type%
|
||||||
|
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
||||||
|
@echo off
|
||||||
|
cd ..
|
||||||
|
call scripts/run_gettext.bat
|
||||||
|
cd %build_dir%
|
||||||
|
cmake --build . --target install --config %build_type%
|
||||||
-1396
File diff suppressed because it is too large
Load Diff
@@ -124,8 +124,6 @@ endif()
|
|||||||
|
|
||||||
if("${CMAKE_GENERATOR_PLATFORM}" MATCHES "x64" OR "${CMAKE_GENERATOR}" MATCHES "Win64")
|
if("${CMAKE_GENERATOR_PLATFORM}" MATCHES "x64" OR "${CMAKE_GENERATOR}" MATCHES "Win64")
|
||||||
set(_arch "x64")
|
set(_arch "x64")
|
||||||
elseif("${CMAKE_GENERATOR_PLATFORM}" MATCHES "ARM64")
|
|
||||||
set(_arch "x64") # GLEW ships one header set; ARM64 uses the x64 import path
|
|
||||||
else()
|
else()
|
||||||
set(_arch "Win32")
|
set(_arch "Win32")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -128,10 +128,6 @@ cmake_minimum_required(VERSION 3.13)
|
|||||||
if(POLICY CMP0074)
|
if(POLICY CMP0074)
|
||||||
cmake_policy(SET CMP0074 NEW)
|
cmake_policy(SET CMP0074 NEW)
|
||||||
endif()
|
endif()
|
||||||
# Re-set after cmake_minimum_required above cleared it; use BoostConfig, not the removed FindBoost.
|
|
||||||
if(POLICY CMP0167)
|
|
||||||
cmake_policy(SET CMP0167 NEW)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(OpenVDB_FIND_QUIETLY)
|
if(OpenVDB_FIND_QUIETLY)
|
||||||
set (_quiet "QUIET")
|
set (_quiet "QUIET")
|
||||||
|
|||||||
@@ -256,13 +256,6 @@ function(add_precompiled_header _target _input)
|
|||||||
message(STATUS "Adding precompiled header ${_input} to target ${_target}.")
|
message(STATUS "Adding precompiled header ${_input} to target ${_target}.")
|
||||||
target_precompile_headers(${_target} PRIVATE ${_input})
|
target_precompile_headers(${_target} PRIVATE ${_input})
|
||||||
|
|
||||||
# Clang records the modification time of every input in the precompiled
|
|
||||||
# header, which makes it differ between two checkouts of the same source
|
|
||||||
# and defeats a compiler cache. The build system already rebuilds the
|
|
||||||
# header when an input changes.
|
|
||||||
target_compile_options(${_target} PRIVATE
|
|
||||||
"$<$<CXX_COMPILER_ID:Clang,AppleClang>:SHELL:-Xclang -fno-pch-timestamp>")
|
|
||||||
|
|
||||||
get_target_property(_sources ${_target} SOURCES)
|
get_target_property(_sources ${_target} SOURCES)
|
||||||
list(FILTER _sources INCLUDE REGEX ".*\\.mm?")
|
list(FILTER _sources INCLUDE REGEX ".*\\.mm?")
|
||||||
|
|
||||||
|
|||||||
Vendored
-43
@@ -1,43 +0,0 @@
|
|||||||
if(CMAKE_VERSION VERSION_LESS 3.22)
|
|
||||||
set(_assimp_url "https://github.com/assimp/assimp/archive/refs/tags/v5.3.1.tar.gz")
|
|
||||||
set(_assimp_hash "SHA256=a07666be71afe1ad4bc008c2336b7c688aca391271188eb9108d0c6db1be53f1")
|
|
||||||
else()
|
|
||||||
set(_assimp_url "https://github.com/assimp/assimp/archive/refs/tags/v5.4.3.tar.gz")
|
|
||||||
set(_assimp_hash "SHA256=66dfbaee288f2bc43172440a55d0235dfc7bf885dda6435c038e8000e79582cb")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# Assimp's bundled zlib (contrib/zlib) is too old to compile against the modern
|
|
||||||
# macOS SDK: its zutil.h takes the classic-Mac branch under TARGET_OS_MAC and
|
|
||||||
# does `#define fdopen(fd,mode) NULL`, which then clobbers the SDK's real
|
|
||||||
# `fdopen` prototype in <stdio.h> and breaks the build. On macOS use the system
|
|
||||||
# zlib (already found by find_package(ZLIB) in deps-unix-common) instead.
|
|
||||||
if(APPLE)
|
|
||||||
set(_assimp_build_zlib "-DASSIMP_BUILD_ZLIB=OFF")
|
|
||||||
else()
|
|
||||||
set(_assimp_build_zlib "-DASSIMP_BUILD_ZLIB=ON")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(Assimp
|
|
||||||
URL ${_assimp_url}
|
|
||||||
URL_HASH ${_assimp_hash}
|
|
||||||
CMAKE_ARGS
|
|
||||||
# Assimp's ccache support sets the global RULE_LAUNCH_COMPILE, which breaks
|
|
||||||
# the Ninja RC rule. The superbuild forwards CMAKE_<LANG>_COMPILER_LAUNCHER.
|
|
||||||
-DASSIMP_BUILD_USE_CCACHE=OFF
|
|
||||||
-DASSIMP_BUILD_TESTS=OFF
|
|
||||||
-DASSIMP_BUILD_SAMPLES=OFF
|
|
||||||
-DASSIMP_BUILD_ASSIMP_TOOLS=OFF
|
|
||||||
-DASSIMP_INSTALL_PDB=OFF
|
|
||||||
-DASSIMP_NO_EXPORT=ON
|
|
||||||
-DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=OFF
|
|
||||||
-DASSIMP_BUILD_GLTF_IMPORTER=ON
|
|
||||||
-DASSIMP_BUILD_OBJ_IMPORTER=ON
|
|
||||||
-DASSIMP_BUILD_FBX_IMPORTER=ON
|
|
||||||
${_assimp_build_zlib}
|
|
||||||
-DASSIMP_WARNINGS_AS_ERRORS=OFF
|
|
||||||
-DBUILD_WITH_STATIC_CRT=OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
add_debug_dep(dep_Assimp)
|
|
||||||
endif ()
|
|
||||||
Vendored
-32
@@ -10,36 +10,7 @@ if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
|||||||
set(_context_arch_line "-DBOOST_CONTEXT_ARCHITECTURE:STRING=${CMAKE_OSX_ARCHITECTURES}")
|
set(_context_arch_line "-DBOOST_CONTEXT_ARCHITECTURE:STRING=${CMAKE_OSX_ARCHITECTURES}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Windows ARM64: Boost.Context's default fcontext implementation assembles .asm
|
|
||||||
# via armasm64, which trips a CMake ASM_ARMASM linker-module bug under the VS
|
|
||||||
# generator. The winfib implementation (Windows Fiber API) avoids assembly while
|
|
||||||
# keeping the Boost::context target that Boost.Asio's stackful coroutines need.
|
|
||||||
set(_context_impl_line "")
|
|
||||||
if (MSVC AND "${DEPS_ARCH}" STREQUAL "arm64")
|
|
||||||
set(_context_impl_line "-DBOOST_CONTEXT_IMPLEMENTATION:STRING=winfib")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(_options "")
|
|
||||||
if (MSVC AND DEP_DEBUG)
|
|
||||||
set(_options "FORWARD_CONFIG")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# Boost.Container's bundled dlmalloc passes int* where the Win32 Interlocked API
|
|
||||||
# takes volatile long*; cl compiles that with a warning, clang errors out.
|
|
||||||
set(_boost_c_flags_line "")
|
|
||||||
set(_boost_cxx_flags_line "")
|
|
||||||
if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
|
||||||
set(_boost_c_flags_line "-DCMAKE_C_FLAGS:STRING=-Wno-incompatible-pointer-types")
|
|
||||||
# The Visual Studio generator applies only the link language's flags to a
|
|
||||||
# project, and boost_container links as C++, so its C file never sees
|
|
||||||
# CMAKE_C_FLAGS. The C++ flags reach every file; keep CMake's defaults.
|
|
||||||
if (CMAKE_GENERATOR MATCHES "Visual Studio")
|
|
||||||
set(_boost_cxx_flags_line "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} -Wno-incompatible-pointer-types")
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(Boost
|
orcaslicer_add_cmake_project(Boost
|
||||||
${_options}
|
|
||||||
URL "https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.gz"
|
URL "https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.gz"
|
||||||
URL_HASH SHA256=4d27e9efed0f6f152dc28db6430b9d3dfb40c0345da7342eaa5a987dde57bd95
|
URL_HASH SHA256=4d27e9efed0f6f152dc28db6430b9d3dfb40c0345da7342eaa5a987dde57bd95
|
||||||
LIST_SEPARATOR |
|
LIST_SEPARATOR |
|
||||||
@@ -51,9 +22,6 @@ orcaslicer_add_cmake_project(Boost
|
|||||||
-DBOOST_IOSTREAMS_ENABLE_ZSTD:BOOL=OFF
|
-DBOOST_IOSTREAMS_ENABLE_ZSTD:BOOL=OFF
|
||||||
"${_context_abi_line}"
|
"${_context_abi_line}"
|
||||||
"${_context_arch_line}"
|
"${_context_arch_line}"
|
||||||
"${_context_impl_line}"
|
|
||||||
"${_boost_c_flags_line}"
|
|
||||||
"${_boost_cxx_flags_line}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DEP_Boost_DEPENDS ZLIB)
|
set(DEP_Boost_DEPENDS ZLIB)
|
||||||
Vendored
+9
-50
@@ -55,7 +55,6 @@ endif ()
|
|||||||
|
|
||||||
set(DEP_DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/DL_CACHE CACHE PATH "Path for downloaded source packages.")
|
set(DEP_DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/DL_CACHE CACHE PATH "Path for downloaded source packages.")
|
||||||
set(FLATPAK FALSE CACHE BOOL "Toggles various build settings for flatpak, like /usr/local in DESTDIR or not building wxwidgets")
|
set(FLATPAK FALSE CACHE BOOL "Toggles various build settings for flatpak, like /usr/local in DESTDIR or not building wxwidgets")
|
||||||
option(SLIC3R_CAD "Build the SolveSpace solver and OCCT ModelingAlgorithms module the parametric Design/CAD tab needs. Must match the main project's SLIC3R_CAD." ON)
|
|
||||||
|
|
||||||
if ("${DESTDIR}" STREQUAL "" OR "${DESTDIR}" STREQUAL "${AUTOGENERATED_DESTDIR}")
|
if ("${DESTDIR}" STREQUAL "" OR "${DESTDIR}" STREQUAL "${AUTOGENERATED_DESTDIR}")
|
||||||
if (LINUX AND (NOT DEFINED USE_OLD_DESTDIR_PREV OR USE_OLD_DESTDIR_PREV) AND EXISTS "${CMAKE_BINARY_DIR}/destdir/usr/local" AND NOT EXISTS "${CMAKE_BINARY_DIR}/OrcaSlicer_dep/usr/local")
|
if (LINUX AND (NOT DEFINED USE_OLD_DESTDIR_PREV OR USE_OLD_DESTDIR_PREV) AND EXISTS "${CMAKE_BINARY_DIR}/destdir/usr/local" AND NOT EXISTS "${CMAKE_BINARY_DIR}/OrcaSlicer_dep/usr/local")
|
||||||
@@ -156,47 +155,26 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
function(orcaslicer_add_cmake_project projectname)
|
function(orcaslicer_add_cmake_project projectname)
|
||||||
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
|
cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
|
||||||
|
|
||||||
# MSVC is true for clang-cl as well, so the sub-build toolchain has to key on the
|
|
||||||
# generator. A non-Visual-Studio superbuild passes its own generator down, and with
|
|
||||||
# it the CMAKE_C_COMPILER / CMAKE_CXX_COMPILER forwarded below.
|
|
||||||
set(_dep_msvc_gen FALSE)
|
|
||||||
if (MSVC AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
|
||||||
set(_dep_msvc_gen TRUE)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||||
if (_is_multi OR _dep_msvc_gen)
|
if (_is_multi OR MSVC)
|
||||||
if (P_ARGS_FORWARD_CONFIG)
|
if (ORCA_INCLUDE_DEBUG_INFO AND NOT DEP_DEBUG)
|
||||||
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
|
||||||
elseif (ORCA_INCLUDE_DEBUG_INFO AND NOT DEP_DEBUG)
|
|
||||||
set(_configs_line "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
set(_configs_line "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||||
else ()
|
else ()
|
||||||
set(_configs_line "")
|
set(_configs_line "")
|
||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (P_ARGS_FORWARD_CONFIG)
|
if (MSVC)
|
||||||
set(_target_config "$<CONFIG>")
|
|
||||||
else()
|
|
||||||
set(_target_config "Release")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (_dep_msvc_gen)
|
|
||||||
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
||||||
# The toolset picks the compiler here, not the CMAKE_<LANG>_COMPILER
|
|
||||||
# forwarded below, so without it a clang-cl superbuild builds with cl.
|
|
||||||
if (CMAKE_GENERATOR_TOOLSET)
|
|
||||||
list(APPEND _gen CMAKE_GENERATOR_TOOLSET "${CMAKE_GENERATOR_TOOLSET}")
|
|
||||||
endif ()
|
|
||||||
else()
|
else()
|
||||||
set(_gen "")
|
set(_gen "")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if ($ENV{CMAKE_BUILD_PARALLEL_LEVEL})
|
if ($ENV{CMAKE_BUILD_PARALLEL_LEVEL})
|
||||||
set(_build_j "") # assume environment will control --build parallel setting
|
set(_build_j "") # assume environment will control --build parallel setting
|
||||||
elseif(_dep_msvc_gen)
|
elseif(MSVC)
|
||||||
set(_build_j "/m")
|
set(_build_j "/m")
|
||||||
else()
|
else()
|
||||||
set(_build_j "-j${NPROC}")
|
set(_build_j "-j${NPROC}")
|
||||||
@@ -218,8 +196,6 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
|
|||||||
-DCMAKE_DEBUG_POSTFIX:STRING=d
|
-DCMAKE_DEBUG_POSTFIX:STRING=d
|
||||||
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
|
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
|
||||||
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
|
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}
|
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER}
|
|
||||||
-DCMAKE_TOOLCHAIN_FILE:STRING=${CMAKE_TOOLCHAIN_FILE}
|
-DCMAKE_TOOLCHAIN_FILE:STRING=${CMAKE_TOOLCHAIN_FILE}
|
||||||
-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}
|
-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}
|
||||||
-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}
|
-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}
|
||||||
@@ -230,8 +206,8 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
|
|||||||
${DEP_CMAKE_OPTS}
|
${DEP_CMAKE_OPTS}
|
||||||
${P_ARGS_CMAKE_ARGS}
|
${P_ARGS_CMAKE_ARGS}
|
||||||
${P_ARGS_UNPARSED_ARGUMENTS}
|
${P_ARGS_UNPARSED_ARGUMENTS}
|
||||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${_target_config} -- ${_build_j}
|
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release -- ${_build_j}
|
||||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${_target_config}
|
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config Release
|
||||||
)
|
)
|
||||||
|
|
||||||
if (FLATPAK)
|
if (FLATPAK)
|
||||||
@@ -265,16 +241,14 @@ else()
|
|||||||
-DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR}
|
-DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR}
|
||||||
-DCMAKE_PREFIX_PATH:STRING=${DESTDIR}
|
-DCMAKE_PREFIX_PATH:STRING=${DESTDIR}
|
||||||
-DCMAKE_IGNORE_PREFIX_PATH:STRING=${CMAKE_IGNORE_PREFIX_PATH}
|
-DCMAKE_IGNORE_PREFIX_PATH:STRING=${CMAKE_IGNORE_PREFIX_PATH}
|
||||||
-DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}
|
|
||||||
-DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER}
|
|
||||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||||
${_cmake_osx_arch}
|
${_cmake_osx_arch}
|
||||||
"${_configs_line}"
|
"${_configs_line}"
|
||||||
${DEP_CMAKE_OPTS}
|
${DEP_CMAKE_OPTS}
|
||||||
${P_ARGS_CMAKE_ARGS}
|
${P_ARGS_CMAKE_ARGS}
|
||||||
${P_ARGS_UNPARSED_ARGUMENTS}
|
${P_ARGS_UNPARSED_ARGUMENTS}
|
||||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${_target_config} -- ${_build_j}
|
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release -- ${_build_j}
|
||||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${_target_config}
|
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config Release
|
||||||
)
|
)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
@@ -364,11 +338,6 @@ include(GLEW/GLEW.cmake)
|
|||||||
|
|
||||||
include(GLFW/GLFW.cmake)
|
include(GLFW/GLFW.cmake)
|
||||||
include(OpenCSG/OpenCSG.cmake)
|
include(OpenCSG/OpenCSG.cmake)
|
||||||
set(SLVS_PKG "")
|
|
||||||
if (SLIC3R_CAD)
|
|
||||||
include(SLVS/SLVS.cmake)
|
|
||||||
set(SLVS_PKG dep_SLVS)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
include(TBB/TBB.cmake)
|
include(TBB/TBB.cmake)
|
||||||
|
|
||||||
@@ -386,9 +355,6 @@ include(libnoise/libnoise.cmake)
|
|||||||
|
|
||||||
include(Draco/Draco.cmake)
|
include(Draco/Draco.cmake)
|
||||||
|
|
||||||
include(FFMPEG/FFMPEG.cmake)
|
|
||||||
include(Assimp/Assimp.cmake)
|
|
||||||
|
|
||||||
|
|
||||||
# I *think* 1.1 is used for *just* md5 hashing?
|
# I *think* 1.1 is used for *just* md5 hashing?
|
||||||
# 3.1 has everything in the right place, but the md5 funcs used are deprecated
|
# 3.1 has everything in the right place, but the md5 funcs used are deprecated
|
||||||
@@ -444,8 +410,6 @@ endif ()
|
|||||||
|
|
||||||
include(OCCT/OCCT.cmake)
|
include(OCCT/OCCT.cmake)
|
||||||
include(OpenCV/OpenCV.cmake)
|
include(OpenCV/OpenCV.cmake)
|
||||||
include(python3/python3.cmake)
|
|
||||||
include(wxInspector/wxInspector.cmake)
|
|
||||||
|
|
||||||
set(_dep_list
|
set(_dep_list
|
||||||
dep_Boost
|
dep_Boost
|
||||||
@@ -458,7 +422,6 @@ set(_dep_list
|
|||||||
dep_NLopt
|
dep_NLopt
|
||||||
dep_OpenVDB
|
dep_OpenVDB
|
||||||
dep_OpenCSG
|
dep_OpenCSG
|
||||||
${SLVS_PKG}
|
|
||||||
dep_OpenCV
|
dep_OpenCV
|
||||||
dep_Eigen
|
dep_Eigen
|
||||||
dep_CGAL
|
dep_CGAL
|
||||||
@@ -469,10 +432,6 @@ set(_dep_list
|
|||||||
${ZLIB_PKG}
|
${ZLIB_PKG}
|
||||||
${EXPAT_PKG}
|
${EXPAT_PKG}
|
||||||
dep_libnoise
|
dep_libnoise
|
||||||
dep_python3
|
|
||||||
dep_wxInspector
|
|
||||||
dep_FFMPEG
|
|
||||||
dep_Assimp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
|||||||
Vendored
-14
@@ -56,18 +56,6 @@ else()
|
|||||||
set(_curl_static ON)
|
set(_curl_static ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# curl 7.75's configure probes and code rely on C laxness cl allows but clang
|
|
||||||
# errors on (implicit function declarations, int* vs u_long* in ioctlsocket),
|
|
||||||
# which flips probe results and misconfigures nonblock.c into the AmigaOS
|
|
||||||
# IoctlSocket branch. Relax both diagnostics so the probes behave like cl, and
|
|
||||||
# pin the camel-case probes off since they only "pass" by implicit declaration.
|
|
||||||
set(_curl_c_flags_line "")
|
|
||||||
set(_curl_probe_overrides "")
|
|
||||||
if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
|
||||||
set(_curl_c_flags_line "-DCMAKE_C_FLAGS:STRING=-Wno-implicit-function-declaration -Wno-incompatible-pointer-types")
|
|
||||||
set(_curl_probe_overrides -DHAVE_IOCTLSOCKET_CAMEL=0 -DHAVE_IOCTLSOCKET_CAMEL_FIONBIO=0)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(CURL
|
orcaslicer_add_cmake_project(CURL
|
||||||
# GIT_REPOSITORY https://github.com/curl/curl.git
|
# GIT_REPOSITORY https://github.com/curl/curl.git
|
||||||
# GIT_TAG curl-7_75_0
|
# GIT_TAG curl-7_75_0
|
||||||
@@ -81,8 +69,6 @@ orcaslicer_add_cmake_project(CURL
|
|||||||
-DBUILD_CURL_EXE:BOOL=OFF
|
-DBUILD_CURL_EXE:BOOL=OFF
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||||
-DCURL_STATICLIB=${_curl_static}
|
-DCURL_STATICLIB=${_curl_static}
|
||||||
"${_curl_c_flags_line}"
|
|
||||||
${_curl_probe_overrides}
|
|
||||||
${_curl_platform_flags}
|
${_curl_platform_flags}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Vendored
-9
@@ -1,13 +1,4 @@
|
|||||||
set(_options "")
|
|
||||||
if (MSVC AND DEP_DEBUG)
|
|
||||||
set(_options "FORWARD_CONFIG")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(Draco
|
orcaslicer_add_cmake_project(Draco
|
||||||
${_options}
|
|
||||||
URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip
|
URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip
|
||||||
URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77
|
URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77
|
||||||
CMAKE_ARGS
|
|
||||||
# The encoder and decoder tools duplicate draco.lib; see deps-windows.cmake.
|
|
||||||
"${DEP_LLD_FORCE_MULTIPLE}"
|
|
||||||
)
|
)
|
||||||
Vendored
-15
@@ -7,20 +7,5 @@ orcaslicer_add_cmake_project(Eigen
|
|||||||
URL https://gitlab.com/libeigen/eigen/-/archive/5.0.1/eigen-5.0.1.zip
|
URL https://gitlab.com/libeigen/eigen/-/archive/5.0.1/eigen-5.0.1.zip
|
||||||
URL_HASH SHA256=0dbb1f9e3aaad66f352c03227d8c983f6f0b49e0b07e71a7300f4abcc01aee12
|
URL_HASH SHA256=0dbb1f9e3aaad66f352c03227d8c983f6f0b49e0b07e71a7300f4abcc01aee12
|
||||||
CMAKE_ARGS "${_eigen_extra_flags}"
|
CMAKE_ARGS "${_eigen_extra_flags}"
|
||||||
# Only the headers are consumed here. Everything below builds nothing we
|
|
||||||
# use, and all three enable_language(Fortran): test/CMakeLists.txt:9,
|
|
||||||
# lapack/CMakeLists.txt:6 and blas/testing/CMakeLists.txt:2. They default
|
|
||||||
# to ON because the dependency configures as its own top-level project.
|
|
||||||
#
|
|
||||||
# Whether that probe is harmless depends on what CMake finds. The Visual
|
|
||||||
# Studio generator supports no Fortran, so it finds nothing; clang-cl sits
|
|
||||||
# next to the LLVM toolset's flang, which works. MSVC with Ninja finds
|
|
||||||
# Strawberry Perl's MinGW gfortran instead, which the deps build already
|
|
||||||
# requires for OpenSSL, and hands it the MSVC-style /machine:x64 that
|
|
||||||
# MinGW's ld reads as a missing input file. The configure dies there and
|
|
||||||
# takes the rest of the superbuild with it.
|
|
||||||
-DEIGEN_BUILD_TESTING=OFF
|
|
||||||
-DEIGEN_BUILD_BLAS=OFF
|
|
||||||
-DEIGEN_BUILD_LAPACK=OFF
|
|
||||||
DEPENDS dep_Boost dep_GMP dep_MPFR
|
DEPENDS dep_Boost dep_GMP dep_MPFR
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
-87
@@ -1,87 +0,0 @@
|
|||||||
set(_conf_cmd ./configure)
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
set(_source_dir "${CMAKE_BINARY_DIR}/dep_FFMPEG-prefix/src/dep_FFMPEG")
|
|
||||||
|
|
||||||
set(PREBUILD_URL_arm64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-winarm64-orca-shared-7.0.zip")
|
|
||||||
set(PREBUILD_HASH_arm64 "12f4140279f2f8469885e1b5b2e8be9d788882914c21523cacd56989f3548054")
|
|
||||||
set(PREBUILD_URL_x64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-win64-orca-shared-7.0.zip")
|
|
||||||
set(PREBUILD_HASH_x64 "e65916020ddb9ef84b2666dfbcbfc9b1d67f69d15b4a66db53754637bf2d498c")
|
|
||||||
|
|
||||||
ExternalProject_Add(dep_FFMPEG
|
|
||||||
URL ${PREBUILD_URL_${DEPS_ARCH}}
|
|
||||||
URL_HASH SHA256=${PREBUILD_HASH_${DEPS_ARCH}}
|
|
||||||
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
|
|
||||||
CONFIGURE_COMMAND ""
|
|
||||||
BUILD_COMMAND ""
|
|
||||||
INSTALL_COMMAND
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/bin" "${DESTDIR}/bin"
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/lib" "${DESTDIR}/lib"
|
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/include" "${DESTDIR}/include"
|
|
||||||
)
|
|
||||||
|
|
||||||
else ()
|
|
||||||
if (APPLE)
|
|
||||||
set(_minos_cmd
|
|
||||||
"--extra-cflags=-mmacosx-version-min=${DEP_OSX_TARGET}"
|
|
||||||
"--extra-ldflags=-mmacosx-version-min=${DEP_OSX_TARGET}"
|
|
||||||
)
|
|
||||||
# Static FFmpeg: nothing to bundle into the .app, no rpath handling.
|
|
||||||
# Disable the VideoToolbox/AudioToolbox HW-accel paths: the player decodes
|
|
||||||
# in software (swscale), and the auto-detected HW objects would drag in
|
|
||||||
# system frameworks that the static libs would then depend on.
|
|
||||||
set(_link_cmd --enable-static --disable-shared --disable-videotoolbox --disable-audiotoolbox)
|
|
||||||
if (IS_CROSS_COMPILE)
|
|
||||||
set(_cross_cmd --enable-cross-compile)
|
|
||||||
set(_pic_cmd --enable-pic)
|
|
||||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64")
|
|
||||||
set(_arch_cmd --arch=arm64)
|
|
||||||
set(_cc_cmd "--cc=clang -arch arm64")
|
|
||||||
else()
|
|
||||||
set(_arch_cmd --arch=x86_64)
|
|
||||||
set(_cc_cmd "--cc=clang -arch x86_64")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
else ()
|
|
||||||
set(_link_cmd --enable-shared)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
set(_build_j -j)
|
|
||||||
if(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL})
|
|
||||||
set(_build_j "-j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
ExternalProject_Add(dep_FFMPEG
|
|
||||||
URL https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.0.3.tar.gz
|
|
||||||
URL_HASH SHA256=DEEDCABE339165214A3637DF4C86A507AEF0D793CF8774FF68735F4737E8DDBC
|
|
||||||
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
|
|
||||||
CONFIGURE_COMMAND ${_conf_cmd}
|
|
||||||
${_cross_cmd}
|
|
||||||
${_pic_cmd}
|
|
||||||
${_arch_cmd}
|
|
||||||
${_cc_cmd}
|
|
||||||
"--prefix=${DESTDIR}"
|
|
||||||
${_link_cmd}
|
|
||||||
${_minos_cmd}
|
|
||||||
--disable-doc
|
|
||||||
--enable-small
|
|
||||||
--disable-outdevs
|
|
||||||
--disable-filters
|
|
||||||
--enable-filter=*null*,afade,*fifo,*format,*resample,aeval,allrgb,allyuv,atempo,pan,*bars,color,*key,crop,draw*,eq*,framerate,*_qsv,*_vaapi,*v4l2*,hw*,scale,volume,test*
|
|
||||||
--disable-protocols
|
|
||||||
--enable-protocol=file,fd,pipe,rtp,udp
|
|
||||||
--disable-muxers
|
|
||||||
--enable-muxer=rtp
|
|
||||||
--disable-encoders
|
|
||||||
--disable-decoders
|
|
||||||
--enable-decoder=*aac*,h264*,mp3*,mjpeg,rv*
|
|
||||||
--disable-demuxers
|
|
||||||
--enable-demuxer=h264,mp3,mov
|
|
||||||
--disable-zlib
|
|
||||||
--disable-avdevice
|
|
||||||
BUILD_IN_SOURCE ON
|
|
||||||
BUILD_COMMAND make ${_build_j}
|
|
||||||
INSTALL_COMMAND make install
|
|
||||||
)
|
|
||||||
|
|
||||||
endif()
|
|
||||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Vendored
-2
@@ -8,8 +8,6 @@ orcaslicer_add_cmake_project(NLopt
|
|||||||
-DNLOPT_GUILE:BOOL=OFF
|
-DNLOPT_GUILE:BOOL=OFF
|
||||||
-DNLOPT_SWIG:BOOL=OFF
|
-DNLOPT_SWIG:BOOL=OFF
|
||||||
-DNLOPT_TESTS:BOOL=OFF
|
-DNLOPT_TESTS:BOOL=OFF
|
||||||
# testopt is built regardless of NLOPT_TESTS; see deps-windows.cmake.
|
|
||||||
"${DEP_LLD_FORCE_MULTIPLE}"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
|||||||
Vendored
-43
@@ -1,20 +1,3 @@
|
|||||||
diff --git a/adm/cmake/occt_defs_flags.cmake b/adm/cmake/occt_defs_flags.cmake
|
|
||||||
index 00000000..00000001 100644
|
|
||||||
--- a/adm/cmake/occt_defs_flags.cmake
|
|
||||||
+++ b/adm/cmake/occt_defs_flags.cmake
|
|
||||||
@@ -134,7 +134,11 @@
|
|
||||||
set (CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}")
|
|
||||||
endif()
|
|
||||||
# Optimize size of binaries
|
|
||||||
- set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-s ${CMAKE_SHARED_LINKER_FLAGS}")
|
|
||||||
+ # clang-cl reports the Clang compiler ID, and OCCT builds shared on Windows,
|
|
||||||
+ # where the MSVC-style linker gets this flag as an argument it does not know.
|
|
||||||
+ if (NOT WIN32)
|
|
||||||
+ set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-s ${CMAKE_SHARED_LINKER_FLAGS}")
|
|
||||||
+ endif()
|
|
||||||
elseif(MINGW)
|
|
||||||
add_definitions(-D_WIN32_WINNT=0x0601)
|
|
||||||
# _WIN32_WINNT=0x0601 (use Windows 7 SDK)
|
|
||||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||||
index d98acc0f..28eb8eb4 100644
|
index d98acc0f..28eb8eb4 100644
|
||||||
--- a/CMakeLists.txt
|
--- a/CMakeLists.txt
|
||||||
@@ -185,32 +168,6 @@ index d98acc0f..28eb8eb4 100644
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
if (BUILD_SAMPLES_QT)
|
if (BUILD_SAMPLES_QT)
|
||||||
diff --git a/adm/cmake/occt_macros.cmake b/adm/cmake/occt_macros.cmake
|
|
||||||
index 224c96b1..8c94a1c5 100644
|
|
||||||
--- a/adm/cmake/occt_macros.cmake
|
|
||||||
+++ b/adm/cmake/occt_macros.cmake
|
|
||||||
@@ -608,7 +608,7 @@ macro (OCCT_INSERT_CODE_FOR_TARGET)
|
|
||||||
install(CODE "if (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$\")
|
|
||||||
set (OCCT_INSTALL_BIN_LETTER \"\")
|
|
||||||
elseif (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Rr][Ee][Ll][Ww][Ii][Tt][Hh][Dd][Ee][Bb][Ii][Nn][Ff][Oo])$\")
|
|
||||||
- set (OCCT_INSTALL_BIN_LETTER \"i\")
|
|
||||||
+ set (OCCT_INSTALL_BIN_LETTER \"\")
|
|
||||||
elseif (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Dd][Ee][Bb][Uu][Gg])$\")
|
|
||||||
set (OCCT_INSTALL_BIN_LETTER \"d\")
|
|
||||||
endif()")
|
|
||||||
diff --git a/adm/cmake/occt_toolkit.cmake b/adm/cmake/occt_toolkit.cmake
|
|
||||||
index 550e0e2f..7ac1a3b8 100644
|
|
||||||
--- a/adm/cmake/occt_toolkit.cmake
|
|
||||||
+++ b/adm/cmake/occt_toolkit.cmake
|
|
||||||
@@ -241,7 +241,7 @@
|
|
||||||
else()
|
|
||||||
set (aReleasePdbConf)
|
|
||||||
endif()
|
|
||||||
- install (FILES ${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin\${OCCT_INSTALL_BIN_LETTER}/${PROJECT_NAME}.pdb
|
|
||||||
+ install (FILES $<TARGET_PDB_FILE:${PROJECT_NAME}>
|
|
||||||
CONFIGURATIONS Debug ${aReleasePdbConf} RelWithDebInfo
|
|
||||||
DESTINATION "${INSTALL_DIR_BIN}\${OCCT_INSTALL_BIN_LETTER}")
|
|
||||||
endif()
|
|
||||||
diff --git a/src/Font/Font_FTFont.cxx b/src/Font/Font_FTFont.cxx
|
diff --git a/src/Font/Font_FTFont.cxx b/src/Font/Font_FTFont.cxx
|
||||||
index 5ae9899f..0a17372b 100644
|
index 5ae9899f..0a17372b 100644
|
||||||
--- a/src/Font/Font_FTFont.cxx
|
--- a/src/Font/Font_FTFont.cxx
|
||||||
|
|||||||
Vendored
+1
-24
@@ -1,31 +1,9 @@
|
|||||||
# clang-cl cannot emit IGESAppli_GeneralModule.cxx on ARM64
|
|
||||||
# (llvm/llvm-project#62081). cl and clang-cl share an ABI.
|
|
||||||
set(_occt_compiler_args "")
|
|
||||||
if ("${DEPS_ARCH}" STREQUAL "arm64" AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|
||||||
set(_occt_compiler_args -DCMAKE_C_COMPILER:STRING=cl -DCMAKE_CXX_COMPILER:STRING=cl)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(library_build_type "Shared")
|
set(library_build_type "Shared")
|
||||||
else()
|
else()
|
||||||
set(library_build_type "Static")
|
set(library_build_type "Static")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# SLIC3R_CAD (declared in deps/CMakeLists.txt) builds OCCT's ModelingAlgorithms module
|
|
||||||
# (fillet/offset/loft), whose only consumer is the parametric Design/CAD tab. With it OFF
|
|
||||||
# the deps prefix matches upstream exactly.
|
|
||||||
#
|
|
||||||
# With it ON the delta is THREE toolkits, not two: TKFillet (7.40 MiB archive, used via
|
|
||||||
# BRepFilletAPI), TKOffset (5.38 MiB, used via BRepOffsetAPI) and TKFeat (4.42 MiB), which
|
|
||||||
# nothing here references but which the module flag builds anyway -- it is all-or-nothing
|
|
||||||
# per module. The module's other nine toolkits are built either way, because DataExchange
|
|
||||||
# (the STEP path upstream already ships) depends on them.
|
|
||||||
#
|
|
||||||
# On macOS/Linux OCCT links statically, so an unreferenced toolkit costs build time and no
|
|
||||||
# shipped bytes. The Windows figure is a real DLL cost and has NOT been measured -- an
|
|
||||||
# earlier "3.77 MiB, Windows only" note here covered only two of the three toolkits and is
|
|
||||||
# not a number to quote. See docs/cad_dependency_weight.md.
|
|
||||||
|
|
||||||
if (IN_GIT_REPO)
|
if (IN_GIT_REPO)
|
||||||
set(OCCT_DIRECTORY_FLAG --directory ${BINARY_DIR_REL}/dep_OCCT-prefix/src/dep_OCCT)
|
set(OCCT_DIRECTORY_FLAG --directory ${BINARY_DIR_REL}/dep_OCCT-prefix/src/dep_OCCT)
|
||||||
endif ()
|
endif ()
|
||||||
@@ -50,10 +28,9 @@ orcaslicer_add_cmake_project(OCCT
|
|||||||
#-DBUILD_MODULE_DataExchange=OFF
|
#-DBUILD_MODULE_DataExchange=OFF
|
||||||
-DBUILD_MODULE_Draw=OFF
|
-DBUILD_MODULE_Draw=OFF
|
||||||
-DBUILD_MODULE_FoundationClasses=OFF
|
-DBUILD_MODULE_FoundationClasses=OFF
|
||||||
-DBUILD_MODULE_ModelingAlgorithms=${SLIC3R_CAD}
|
-DBUILD_MODULE_ModelingAlgorithms=OFF
|
||||||
-DBUILD_MODULE_ModelingData=OFF
|
-DBUILD_MODULE_ModelingData=OFF
|
||||||
-DBUILD_MODULE_Visualization=OFF
|
-DBUILD_MODULE_Visualization=OFF
|
||||||
${_occt_compiler_args}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# add_dependencies(dep_OCCT ${FREETYPE_PKG})
|
# add_dependencies(dep_OCCT ${FREETYPE_PKG})
|
||||||
|
|||||||
Vendored
+1
-16
@@ -1,20 +1,7 @@
|
|||||||
# Intel IPP / IPP-ICV is x86/x64 only — there is no ARM64 build, so enabling it
|
if (MSVC)
|
||||||
# leaves ~200 unresolved ippicv* externals at link time on Windows ARM64.
|
|
||||||
if (MSVC AND NOT "${DEPS_ARCH}" STREQUAL "arm64")
|
|
||||||
set(_use_IPP "-DWITH_IPP=ON")
|
set(_use_IPP "-DWITH_IPP=ON")
|
||||||
if (DEP_DEBUG)
|
|
||||||
set(_options "FORWARD_CONFIG")
|
|
||||||
endif ()
|
|
||||||
else ()
|
else ()
|
||||||
set(_use_IPP "-DWITH_IPP=OFF")
|
set(_use_IPP "-DWITH_IPP=OFF")
|
||||||
set(_options "")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
# carotene is OpenCV's ARM NEON HAL. It uses M_PI without _USE_MATH_DEFINES
|
|
||||||
# and does not compile with clang-cl.
|
|
||||||
set(_disable_carotene "")
|
|
||||||
if ("${DEPS_ARCH}" STREQUAL "arm64" AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|
||||||
set(_disable_carotene "-DWITH_CAROTENE=OFF")
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (IN_GIT_REPO)
|
if (IN_GIT_REPO)
|
||||||
@@ -22,7 +9,6 @@ if (IN_GIT_REPO)
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(OpenCV
|
orcaslicer_add_cmake_project(OpenCV
|
||||||
${_options}
|
|
||||||
URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.tar.gz
|
URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.tar.gz
|
||||||
URL_HASH SHA256=1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277
|
URL_HASH SHA256=1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277
|
||||||
PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch
|
PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch
|
||||||
@@ -90,6 +76,5 @@ orcaslicer_add_cmake_project(OpenCV
|
|||||||
-DWITH_PROTOBUF=OFF
|
-DWITH_PROTOBUF=OFF
|
||||||
-DWITH_WIN32UI=OFF
|
-DWITH_WIN32UI=OFF
|
||||||
-DHAVE_WIN32UI=FALSE
|
-DHAVE_WIN32UI=FALSE
|
||||||
${_disable_carotene}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Vendored
-12
@@ -32,17 +32,6 @@ else()
|
|||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
set(_patch_cmd ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/0001-OpenEXR-GCC13.patch)
|
set(_patch_cmd ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/0001-OpenEXR-GCC13.patch)
|
||||||
elseif (MSVC AND "${DEPS_ARCH}" STREQUAL "arm64")
|
|
||||||
# Windows ARM64: OpenEXR 2.5.5 hard-codes IMF_HAVE_SSE2 for any MSVC
|
|
||||||
# (ImfSimd.h: `_MSC_VER >= 1300`), pulling in <emmintrin.h> (x86-only) -> C1189.
|
|
||||||
# Patch the header to require an x86 target, and force the SSE cache vars off.
|
|
||||||
set(_patch_cmd ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/patch_openexr_arm64.cmake)
|
|
||||||
set(_openexr_arm64_args
|
|
||||||
-DOPENEXR_IMF_HAVE_SSE2:BOOL=OFF
|
|
||||||
-DOPENEXR_IMF_HAVE_SSSE3:BOOL=OFF
|
|
||||||
-DILMBASE_HAVE_SSE:BOOL=OFF
|
|
||||||
-DILMBASE_FORCE_DISABLE_INTEL_SSE:BOOL=ON
|
|
||||||
)
|
|
||||||
else ()
|
else ()
|
||||||
set(_patch_cmd "")
|
set(_patch_cmd "")
|
||||||
endif ()
|
endif ()
|
||||||
@@ -60,7 +49,6 @@ orcaslicer_add_cmake_project(OpenEXR
|
|||||||
-DPYILMBASE_ENABLE:BOOL=OFF
|
-DPYILMBASE_ENABLE:BOOL=OFF
|
||||||
-DOPENEXR_VIEWERS_ENABLE:BOOL=OFF
|
-DOPENEXR_VIEWERS_ENABLE:BOOL=OFF
|
||||||
-DOPENEXR_BUILD_UTILS:BOOL=OFF
|
-DOPENEXR_BUILD_UTILS:BOOL=OFF
|
||||||
${_openexr_arm64_args}
|
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
# Applied as PATCH_COMMAND for OpenEXR 2.5.5 on Windows ARM64.
|
|
||||||
#
|
|
||||||
# Root cause of the ARM64 build failure: OpenEXR/IlmImf/ImfSimd.h hard-codes
|
|
||||||
# #if defined __SSE2__ || (_MSC_VER >= 1300 && !_M_CEE_PURE)
|
|
||||||
# #define IMF_HAVE_SSE2 1
|
|
||||||
# #endif
|
|
||||||
# The `_MSC_VER >= 1300` arm is true for *every* MSVC, including ARM64, so
|
|
||||||
# IMF_HAVE_SSE2 gets defined and <emmintrin.h> (an x86-only header) is pulled
|
|
||||||
# in -> error C1189. This is a pure-preprocessor decision, so no CMake cache
|
|
||||||
# variable can suppress it. Patch the header to also require an x86 target.
|
|
||||||
|
|
||||||
set(_simd "OpenEXR/IlmImf/ImfSimd.h")
|
|
||||||
if(EXISTS "${_simd}")
|
|
||||||
file(READ "${_simd}" _content)
|
|
||||||
set(_old "#if defined __SSE2__ || (_MSC_VER >= 1300 && !_M_CEE_PURE)")
|
|
||||||
set(_new "#if (defined __SSE2__ || (_MSC_VER >= 1300 && !_M_CEE_PURE)) && (defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__))")
|
|
||||||
if(_content MATCHES "_M_IX86")
|
|
||||||
message(STATUS "[ARM64 patch] ImfSimd.h already guarded")
|
|
||||||
else()
|
|
||||||
string(REPLACE "${_old}" "${_new}" _patched "${_content}")
|
|
||||||
if(_patched STREQUAL _content)
|
|
||||||
message(FATAL_ERROR "[ARM64 patch] Failed to match SSE2 guard in ${_simd}")
|
|
||||||
endif()
|
|
||||||
file(WRITE "${_simd}" "${_patched}")
|
|
||||||
message(STATUS "[ARM64 patch] Guarded IMF_HAVE_SSE2 with x86 arch check in ${_simd}")
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "[ARM64 patch] Not found: ${_simd}")
|
|
||||||
endif()
|
|
||||||
Vendored
+4
-32
@@ -6,31 +6,17 @@ if(DEFINED OPENSSL_ARCH)
|
|||||||
set(_cross_arch ${OPENSSL_ARCH})
|
set(_cross_arch ${OPENSSL_ARCH})
|
||||||
else()
|
else()
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
if("${DEPS_ARCH}" STREQUAL "arm64")
|
set(_cross_arch "VC-WIN64A")
|
||||||
set(_cross_arch "VC-WIN64-ARM")
|
|
||||||
else()
|
|
||||||
set(_cross_arch "VC-WIN64A")
|
|
||||||
endif()
|
|
||||||
elseif(APPLE)
|
elseif(APPLE)
|
||||||
set(_cross_arch "darwin64-${CMAKE_OSX_ARCHITECTURES}-cc")
|
set(_cross_arch "darwin64-${CMAKE_OSX_ARCHITECTURES}-cc")
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
set(_openssl_msvc_env CC=cl CXX=cl RC=rc CL=/FS)
|
set(_conf_cmd perl Configure )
|
||||||
# OpenSSL's perl Configure honors the CC environment variable, but the
|
|
||||||
# VC-WIN64A makefile only works with cl (an unquoted clang-cl path with
|
|
||||||
# spaces, e.g. exported by CLion, silently produces no .obj files and the
|
|
||||||
# lib step fails with LNK1181). Pin the upstream toolchain.
|
|
||||||
# Keep rc.exe resolved from the MSVC developer environment as well. The
|
|
||||||
# absolute Windows SDK path contains spaces and OpenSSL 1.1.1 writes it to
|
|
||||||
# the generated nmake file without quoting, which skips .res generation.
|
|
||||||
# /FS serializes access to OpenSSL's shared generated PDB when cl is
|
|
||||||
# driven through nmake from a Ninja configure step.
|
|
||||||
set(_conf_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} perl Configure )
|
|
||||||
set(_cross_comp_prefix_line "")
|
set(_cross_comp_prefix_line "")
|
||||||
set(_make_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake)
|
set(_make_cmd nmake)
|
||||||
set(_install_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake install_sw )
|
set(_install_cmd nmake install_sw )
|
||||||
else()
|
else()
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
set(_conf_cmd export MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} && ./Configure -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET})
|
set(_conf_cmd export MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} && ./Configure -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET})
|
||||||
@@ -62,14 +48,6 @@ ExternalProject_Add(dep_OpenSSL
|
|||||||
CONFIGURE_COMMAND ${_conf_cmd} ${_cross_arch}
|
CONFIGURE_COMMAND ${_conf_cmd} ${_cross_arch}
|
||||||
"--openssldir=${DESTDIR}"
|
"--openssldir=${DESTDIR}"
|
||||||
"--prefix=${DESTDIR}"
|
"--prefix=${DESTDIR}"
|
||||||
# OpenSSL's linux-x86_64 target sets multilib=64, so it installs to
|
|
||||||
# <prefix>/lib64 while every other dep uses <prefix>/lib. CPython's
|
|
||||||
# --with-openssl only ever emits -L<dir>/lib, so it misses the bundled
|
|
||||||
# static libs and silently links the system OpenSSL instead -- which,
|
|
||||||
# against 1.1.1w headers, leaves _ssl.so with an undefined
|
|
||||||
# SSL_get_peer_certificate (removed in OpenSSL 3.x). Pin libdir so the
|
|
||||||
# prefix stays single-layout.
|
|
||||||
"--libdir=lib"
|
|
||||||
${_cross_comp_prefix_line}
|
${_cross_comp_prefix_line}
|
||||||
no-shared
|
no-shared
|
||||||
no-asm
|
no-asm
|
||||||
@@ -80,12 +58,6 @@ ExternalProject_Add(dep_OpenSSL
|
|||||||
INSTALL_COMMAND ${_install_cmd}
|
INSTALL_COMMAND ${_install_cmd}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CMAKE_GENERATOR MATCHES "Visual Studio")
|
|
||||||
# OpenSSL builds with cl, but MSBuild runs nmake in this project's toolset
|
|
||||||
# environment, and ClangCL's puts clang's headers first. Use the default.
|
|
||||||
set_target_properties(dep_OpenSSL PROPERTIES VS_PLATFORM_TOOLSET "$(DefaultPlatformToolset)")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
ExternalProject_Add_Step(dep_OpenSSL install_cmake_files
|
ExternalProject_Add_Step(dep_OpenSSL install_cmake_files
|
||||||
DEPENDEES install
|
DEPENDEES install
|
||||||
|
|
||||||
|
|||||||
Vendored
-4
@@ -1,10 +1,6 @@
|
|||||||
if (APPLE)
|
if (APPLE)
|
||||||
# Only disable NEON extension for Apple ARM builds, leave it enabled for Raspberry PI.
|
# Only disable NEON extension for Apple ARM builds, leave it enabled for Raspberry PI.
|
||||||
set(_disable_neon_extension "-DPNG_ARM_NEON=off")
|
set(_disable_neon_extension "-DPNG_ARM_NEON=off")
|
||||||
elseif ("${DEPS_ARCH}" STREQUAL "arm64" AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
|
||||||
# libpng's CMake ignores PNG_ARM_NEON on Windows ARM64 and skips the NEON
|
|
||||||
# sources, but pngpriv.h enables NEON anyway.
|
|
||||||
set(_disable_neon_extension "-DCMAKE_C_FLAGS=/DWIN32 /D_WINDOWS /DPNG_ARM_NEON_OPT=0")
|
|
||||||
else ()
|
else ()
|
||||||
set(_disable_neon_extension "")
|
set(_disable_neon_extension "")
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
Vendored
-65
@@ -1,65 +0,0 @@
|
|||||||
# Replaces the upstream SolveSpaceLib CMakeLists, which builds a demo executable and
|
|
||||||
# has no install rules. The sources themselves are used verbatim.
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
|
|
||||||
project(SLVS VERSION 3.0)
|
|
||||||
|
|
||||||
add_library(slvs
|
|
||||||
libslvs/constrainteq.cpp
|
|
||||||
libslvs/entity.cpp
|
|
||||||
libslvs/expr.cpp
|
|
||||||
libslvs/system.cpp
|
|
||||||
libslvs/util.cpp
|
|
||||||
libslvs/platform/unixutil.cpp
|
|
||||||
libslvs/lib.cpp
|
|
||||||
libslvs/SolveSpaceSystem.cpp)
|
|
||||||
|
|
||||||
target_compile_features(slvs PUBLIC cxx_std_11)
|
|
||||||
|
|
||||||
# LIBRARY strips the solver core out of the SolveSpace application it was extracted from.
|
|
||||||
target_compile_definitions(slvs PRIVATE -DLIBRARY)
|
|
||||||
if (MSVC)
|
|
||||||
target_compile_definitions(slvs PRIVATE -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
target_include_directories(slvs
|
|
||||||
PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/libslvs/include>
|
|
||||||
PRIVATE ${PROJECT_SOURCE_DIR}/libslvs)
|
|
||||||
|
|
||||||
# libslic3r is linked into shared targets, so this has to be position independent.
|
|
||||||
set_target_properties(slvs PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
||||||
|
|
||||||
# 2018 code, predating the project's warning settings; it is not ours to clean up.
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
||||||
target_compile_options(slvs PRIVATE -w -fno-strict-aliasing)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
write_basic_package_version_file(
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
|
||||||
VERSION ${PROJECT_VERSION}
|
|
||||||
COMPATIBILITY AnyNewerVersion)
|
|
||||||
|
|
||||||
install(TARGETS slvs
|
|
||||||
EXPORT ${PROJECT_NAME}Targets
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
||||||
|
|
||||||
set(ConfigPackageLocation ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
|
|
||||||
|
|
||||||
install(EXPORT ${PROJECT_NAME}Targets
|
|
||||||
FILE "${PROJECT_NAME}Config.cmake"
|
|
||||||
NAMESPACE ${PROJECT_NAME}::
|
|
||||||
DESTINATION ${ConfigPackageLocation})
|
|
||||||
|
|
||||||
install(FILES
|
|
||||||
${PROJECT_SOURCE_DIR}/libslvs/include/slvs.h
|
|
||||||
${PROJECT_SOURCE_DIR}/libslvs/include/SolveSpaceSystem.h
|
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
||||||
|
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
|
||||||
DESTINATION ${ConfigPackageLocation})
|
|
||||||
Vendored
-13
@@ -1,13 +0,0 @@
|
|||||||
# libslvs — the geometric constraint solver behind the Design tab's sketch constraints.
|
|
||||||
# Extraction of solvespace.com's libslvs, taken verbatim from JacobStoren/SolveSpaceLib;
|
|
||||||
# only the CMakeLists is ours, because upstream's builds a demo and installs nothing.
|
|
||||||
# GPLv3, compatible with this fork's licence. Self-contained: no external dependencies.
|
|
||||||
orcaslicer_add_cmake_project(SLVS
|
|
||||||
URL https://github.com/JacobStoren/SolveSpaceLib/archive/4d8704523e4bf212fadf5189f92484244f670fea.zip
|
|
||||||
URL_HASH SHA256=1c4bdde9c3c6ef20ea4b50b73601de56769f2eb131b36927d7c6489f102e6c30
|
|
||||||
PATCH_COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in ./CMakeLists.txt
|
|
||||||
)
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
add_debug_dep(dep_SLVS)
|
|
||||||
endif ()
|
|
||||||
Vendored
-98
@@ -1,98 +0,0 @@
|
|||||||
# Copyright (c) 2020-2021 Intel Corporation
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
# you may not use this file except in compliance with the License.
|
|
||||||
# You may obtain a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
# See the License for the specific language governing permissions and
|
|
||||||
# limitations under the License.
|
|
||||||
|
|
||||||
set(TBB_LINK_DEF_FILE_FLAG ${CMAKE_LINK_DEF_FILE_FLAG})
|
|
||||||
set(TBB_DEF_FILE_PREFIX win${TBB_ARCH})
|
|
||||||
|
|
||||||
# Workaround for CMake issue https://gitlab.kitware.com/cmake/cmake/issues/18317.
|
|
||||||
# TODO: consider use of CMP0092 CMake policy.
|
|
||||||
string(REGEX REPLACE "/W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
|
|
||||||
|
|
||||||
set(TBB_WARNING_LEVEL $<$<BOOL:${TBB_STRICT}>:/W4> $<$<BOOL:${TBB_STRICT}>:/WX>)
|
|
||||||
|
|
||||||
# Warning suppression C4324: structure was padded due to alignment specifier
|
|
||||||
set(TBB_WARNING_SUPPRESS /wd4324)
|
|
||||||
set(TBB_TEST_COMPILE_FLAGS /bigobj)
|
|
||||||
|
|
||||||
if (MSVC_VERSION LESS_EQUAL 1900)
|
|
||||||
# Warning suppression C4503 for VS2015 and earlier:
|
|
||||||
# decorated name length exceeded, name was truncated.
|
|
||||||
# More info can be found at
|
|
||||||
# https://docs.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4503
|
|
||||||
set(TBB_TEST_COMPILE_FLAGS ${TBB_TEST_COMPILE_FLAGS} /wd4503)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(TBB_LIB_COMPILE_FLAGS -D_CRT_SECURE_NO_WARNINGS /GS)
|
|
||||||
set(TBB_COMMON_COMPILE_FLAGS /volatile:iso /FS /EHsc)
|
|
||||||
|
|
||||||
# Ignore /WX set through add_compile_options() or added to CMAKE_CXX_FLAGS if TBB_STRICT is disabled.
|
|
||||||
if (NOT TBB_STRICT AND COMMAND tbb_remove_compile_flag)
|
|
||||||
tbb_remove_compile_flag(/WX)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WINDOWS_STORE OR TBB_WINDOWS_DRIVER)
|
|
||||||
set(TBB_COMMON_COMPILE_FLAGS ${TBB_COMMON_COMPILE_FLAGS} /D_WIN32_WINNT=0x0A00)
|
|
||||||
set(TBB_COMMON_LINK_FLAGS -NODEFAULTLIB:kernel32.lib -INCREMENTAL:NO)
|
|
||||||
set(TBB_COMMON_LINK_LIBS OneCore.lib)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (WINDOWS_STORE)
|
|
||||||
if (NOT CMAKE_SYSTEM_VERSION EQUAL 10.0)
|
|
||||||
message(FATAL_ERROR "CMAKE_SYSTEM_VERSION must be equal to 10.0")
|
|
||||||
endif()
|
|
||||||
set(TBB_COMMON_COMPILE_FLAGS ${TBB_COMMON_COMPILE_FLAGS} /ZW /ZW:nostdlib)
|
|
||||||
# CMake define this extra lib, remove it for this build type
|
|
||||||
string(REGEX REPLACE "WindowsApp.lib" "" CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES}")
|
|
||||||
|
|
||||||
if (TBB_NO_APPCONTAINER)
|
|
||||||
set(TBB_LIB_LINK_FLAGS ${TBB_LIB_LINK_FLAGS} -APPCONTAINER:NO)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (TBB_WINDOWS_DRIVER)
|
|
||||||
# Since this is universal driver disable this variable
|
|
||||||
set(CMAKE_SYSTEM_PROCESSOR "")
|
|
||||||
# CMake define list additional libs, remove it for this build type
|
|
||||||
set(CMAKE_CXX_STANDARD_LIBRARIES "")
|
|
||||||
set(TBB_COMMON_COMPILE_FLAGS ${TBB_COMMON_COMPILE_FLAGS} /D _UNICODE /DUNICODE /DWINAPI_FAMILY=WINAPI_FAMILY_APP /D__WRL_NO_DEFAULT_LIB__)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (NOT DEFINED TBB_ENABLE_IPO)
|
|
||||||
if (DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
|
||||||
set(TBB_ENABLE_IPO ${CMAKE_INTERPROCEDURAL_OPTIMIZATION})
|
|
||||||
else()
|
|
||||||
set(TBB_ENABLE_IPO ON)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (TBB_ENABLE_IPO)
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "(Clang|IntelLLVM)")
|
|
||||||
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86|AMD64)")
|
|
||||||
set(TBB_COMMON_COMPILE_FLAGS ${TBB_COMMON_COMPILE_FLAGS} -mrtm -mwaitpkg)
|
|
||||||
endif()
|
|
||||||
set(TBB_OPENMP_NO_LINK_FLAG TRUE)
|
|
||||||
set(TBB_IPO_COMPILE_FLAGS $<$<NOT:$<CONFIG:Debug>>:-flto>)
|
|
||||||
else()
|
|
||||||
set(TBB_IPO_COMPILE_FLAGS $<$<NOT:$<CONFIG:Debug>>:/GL>)
|
|
||||||
set(TBB_IPO_LINK_FLAGS $<$<NOT:$<CONFIG:Debug>>:-LTCG> $<$<NOT:$<CONFIG:Debug>>:-INCREMENTAL:NO>)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
if (CMAKE_CXX_COMPILER_ID MATCHES "(Clang|IntelLLVM)" AND CMAKE_SYSTEM_PROCESSOR MATCHES "(x86|AMD64)")
|
|
||||||
set(TBB_COMMON_COMPILE_FLAGS ${TBB_COMMON_COMPILE_FLAGS} -mrtm -mwaitpkg)
|
|
||||||
endif()
|
|
||||||
set(TBB_IPO_COMPILE_FLAGS "")
|
|
||||||
set(TBB_IPO_LINK_FLAGS "")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(TBB_OPENMP_FLAG /openmp)
|
|
||||||
Vendored
+1
-5
@@ -1,6 +1,4 @@
|
|||||||
if (MSVC)
|
if (FLATPAK AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||||
set(_patch_command ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/MSVC.cmake ./cmake/compilers/MSVC.cmake)
|
|
||||||
elseif (FLATPAK AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
|
||||||
set(_patch_command ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/GNU.cmake ./cmake/compilers/GNU.cmake)
|
set(_patch_command ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/GNU.cmake ./cmake/compilers/GNU.cmake)
|
||||||
else()
|
else()
|
||||||
set(_patch_command "")
|
set(_patch_command "")
|
||||||
@@ -15,8 +13,6 @@ orcaslicer_add_cmake_project(
|
|||||||
-DTBB_BUILD_SHARED=OFF
|
-DTBB_BUILD_SHARED=OFF
|
||||||
-DTBB_BUILD_TESTS=OFF
|
-DTBB_BUILD_TESTS=OFF
|
||||||
-DTBB_TEST=OFF
|
-DTBB_TEST=OFF
|
||||||
-DTBB_ENABLE_IPO=OFF
|
|
||||||
-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=OFF
|
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||||
-DCMAKE_DEBUG_POSTFIX=_debug
|
-DCMAKE_DEBUG_POSTFIX=_debug
|
||||||
)
|
)
|
||||||
|
|||||||
Vendored
-9
@@ -42,15 +42,6 @@ else ()
|
|||||||
message(FATAL_ERROR "Unsupported OS architecture: ${DEPS_ARCH}")
|
message(FATAL_ERROR "Unsupported OS architecture: ${DEPS_ARCH}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
# Draco's tools and NLopt's testopt compile sources that are also in their
|
|
||||||
# static library. MSBuild passes the library before the objects and lld-link
|
|
||||||
# resolves as it goes, so the library's copy wins and the object then reads as
|
|
||||||
# a duplicate. Nothing uses those executables, so let lld keep the first one.
|
|
||||||
set(DEP_LLD_FORCE_MULTIPLE "")
|
|
||||||
if (CMAKE_GENERATOR MATCHES "Visual Studio" AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|
||||||
set(DEP_LLD_FORCE_MULTIPLE "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (${DEP_DEBUG})
|
if (${DEP_DEBUG})
|
||||||
set(DEP_BOOST_DEBUG "debug")
|
set(DEP_BOOST_DEBUG "debug")
|
||||||
else ()
|
else ()
|
||||||
|
|||||||
Vendored
-71
@@ -1,71 +0,0 @@
|
|||||||
From b32e63f75046d186d93ee7d627c978f2f7c892f3 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Steve Dower <steve.dower@python.org>
|
|
||||||
Date: Fri, 10 Jul 2026 14:04:00 +0100
|
|
||||||
Subject: [PATCH] [3.13] gh-153438: Update NuGet download URL (GH-153482)
|
|
||||||
(GH-153515)
|
|
||||||
|
|
||||||
gh-153438: Update NuGet download URL (GH-153482)
|
|
||||||
(cherry picked from commit 106eb532ea3b243423e62a702719e9d3c0e40c16)
|
|
||||||
(cherry picked from commit 62eb50d65d538dda5164f9c4a097a896454a0d1c)
|
|
||||||
|
|
||||||
Co-authored-by: Steve Dower <steve.dower@python.org>
|
|
||||||
Co-authored-by: Harjoth Khara <harjoth.khara@gmail.com>
|
|
||||||
---
|
|
||||||
Doc/using/windows.rst | 6 +++---
|
|
||||||
.../Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst | 2 ++
|
|
||||||
PCbuild/find_python.bat | 2 +-
|
|
||||||
Tools/msi/get_externals.bat | 2 +-
|
|
||||||
4 files changed, 7 insertions(+), 5 deletions(-)
|
|
||||||
create mode 100644 Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst
|
|
||||||
|
|
||||||
diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst
|
|
||||||
index 2a507675044666..4800789ca2d6cc 100644
|
|
||||||
--- a/Doc/using/windows.rst
|
|
||||||
+++ b/Doc/using/windows.rst
|
|
||||||
@@ -414,9 +414,9 @@ on using nuget. What follows is a summary that is sufficient for Python
|
|
||||||
developers.
|
|
||||||
|
|
||||||
The ``nuget.exe`` command line tool may be downloaded directly from
|
|
||||||
-``https://aka.ms/nugetclidl``, for example, using curl or PowerShell. With the
|
|
||||||
-tool, the latest version of Python for 64-bit or 32-bit machines is installed
|
|
||||||
-using::
|
|
||||||
+``https://dist.nuget.org/win-x86-commandline/latest/nuget.exe``, for example,
|
|
||||||
+using curl or PowerShell. With the tool, the latest version of Python for
|
|
||||||
+64-bit or 32-bit machines is installed using::
|
|
||||||
|
|
||||||
nuget.exe install python -ExcludeVersion -OutputDirectory .
|
|
||||||
nuget.exe install pythonx86 -ExcludeVersion -OutputDirectory .
|
|
||||||
diff --git a/Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst b/Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst
|
|
||||||
new file mode 100644
|
|
||||||
index 00000000000000..edab8e6ba7b259
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/Misc/NEWS.d/next/Build/2026-07-09-22-45-00.gh-issue-153438.Qr7N2p.rst
|
|
||||||
@@ -0,0 +1,2 @@
|
|
||||||
+Update Windows build and installer tooling and documentation to use the
|
|
||||||
+current download URL for ``nuget.exe``.
|
|
||||||
diff --git a/PCbuild/find_python.bat b/PCbuild/find_python.bat
|
|
||||||
index 0af367a3efafad..9612a2860ef9c3 100644
|
|
||||||
--- a/PCbuild/find_python.bat
|
|
||||||
+++ b/PCbuild/find_python.bat
|
|
||||||
@@ -52,7 +52,7 @@
|
|
||||||
@set _Py_HOST_PYTHON=%HOST_PYTHON%
|
|
||||||
@if "%_Py_HOST_PYTHON%"=="" set _Py_HOST_PYTHON=py
|
|
||||||
@if "%_Py_NUGET%"=="" (set _Py_NUGET=%_Py_EXTERNALS_DIR%\nuget.exe)
|
|
||||||
-@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://aka.ms/nugetclidl)
|
|
||||||
+@if "%_Py_NUGET_URL%"=="" (set _Py_NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe)
|
|
||||||
@if NOT exist "%_Py_NUGET%" (
|
|
||||||
@if not "%_Py_Quiet%"=="1" @echo Downloading nuget...
|
|
||||||
@rem NB: Must use single quotes around NUGET here, NOT double!
|
|
||||||
diff --git a/Tools/msi/get_externals.bat b/Tools/msi/get_externals.bat
|
|
||||||
index f6602ce9588ff4..c7c7e9f470bc25 100644
|
|
||||||
--- a/Tools/msi/get_externals.bat
|
|
||||||
+++ b/Tools/msi/get_externals.bat
|
|
||||||
@@ -6,7 +6,7 @@ set HERE=%~dp0
|
|
||||||
if "%PCBUILD%"=="" (set PCBUILD=%HERE%..\..\PCbuild\)
|
|
||||||
if "%EXTERNALS_DIR%"=="" (set EXTERNALS_DIR=%HERE%..\..\externals\windows-installer)
|
|
||||||
if "%NUGET%"=="" (set NUGET=%EXTERNALS_DIR%\..\nuget.exe)
|
|
||||||
-if "%NUGET_URL%"=="" (set NUGET_URL=https://aka.ms/nugetclidl)
|
|
||||||
+if "%NUGET_URL%"=="" (set NUGET_URL=https://dist.nuget.org/win-x86-commandline/latest/nuget.exe)
|
|
||||||
|
|
||||||
set DO_FETCH=true
|
|
||||||
set DO_CLEAN=false
|
|
||||||
-12
@@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!-- Compiles Objects/unicodectype.c without optimisation. VS 2026's ARM64 code
|
|
||||||
generator needs about 27 GB for _PyUnicode_ToNumeric, a switch with 1951
|
|
||||||
cases. CPython has the same workaround (python/cpython#153668). -->
|
|
||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Update="..\Objects\unicodectype.c">
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
|
||||||
</ClCompile>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
Vendored
-291
@@ -1,291 +0,0 @@
|
|||||||
|
|
||||||
include(ProcessorCount)
|
|
||||||
ProcessorCount(NPROC)
|
|
||||||
|
|
||||||
set(_python_version "3.12.13")
|
|
||||||
string(REGEX REPLACE "^([0-9]+\\.[0-9]+)\\..*" "\\1" _python_version_short "${_python_version}")
|
|
||||||
set(_python_url "https://www.python.org/ftp/python/${_python_version}/Python-${_python_version}.tar.xz")
|
|
||||||
set(_python_sha256 "c08bc65a81971c1dd5783182826503369466c7e67374d1646519adf05207b684")
|
|
||||||
|
|
||||||
|
|
||||||
set(_patch_cmd "")
|
|
||||||
if(WIN32)
|
|
||||||
|
|
||||||
# Fix python build failure on Windows if python is not available, due to wrong nuget download URL
|
|
||||||
# See https://github.com/python/cpython/issues/153438
|
|
||||||
# Patch from https://github.com/python/cpython/pull/153608
|
|
||||||
# This patch has not been merged to 3.12 yet so we need to apply it manually
|
|
||||||
#
|
|
||||||
# Without core.autocrlf=false the patched find_python.bat comes out LF and
|
|
||||||
# cmd.exe cannot find its goto labels.
|
|
||||||
set(_patch_cmd git init
|
|
||||||
&& ${GIT_EXECUTABLE} -c core.autocrlf=false apply --verbose
|
|
||||||
--ignore-space-change --whitespace=fix
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/01-windows-nuget.patch)
|
|
||||||
|
|
||||||
if(MSVC_VERSION EQUAL 1800)
|
|
||||||
set(_python_platform_toolset v120)
|
|
||||||
elseif(MSVC_VERSION EQUAL 1900)
|
|
||||||
set(_python_platform_toolset v140)
|
|
||||||
elseif(MSVC_VERSION LESS 1920)
|
|
||||||
set(_python_platform_toolset v141)
|
|
||||||
elseif(MSVC_VERSION LESS 1930)
|
|
||||||
set(_python_platform_toolset v142)
|
|
||||||
elseif(MSVC_VERSION LESS 1950)
|
|
||||||
set(_python_platform_toolset v143)
|
|
||||||
elseif(MSVC_VERSION LESS 1960)
|
|
||||||
set(_python_platform_toolset v145)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Unsupported MSVC version for CPython build: ${MSVC_VERSION}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 64-bit-hosted MSBuild selection (see the build-step comment below). Default
|
|
||||||
# to the amd64 host + x64 tools; only the native ARM64 build differs.
|
|
||||||
set(_python_msbuild_host amd64)
|
|
||||||
set(_python_tool_arch x64)
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
|
|
||||||
set(_python_pcbuild_platform ARM64)
|
|
||||||
set(_python_layout_arch arm64)
|
|
||||||
set(_python_pcbuild_output_dir arm64)
|
|
||||||
set(_python_msbuild_host arm64) # native ARM64 MSBuild already hosts arm64 tools
|
|
||||||
set(_python_tool_arch "")
|
|
||||||
elseif(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
set(_python_pcbuild_platform x64)
|
|
||||||
set(_python_layout_arch amd64)
|
|
||||||
set(_python_pcbuild_output_dir amd64)
|
|
||||||
else()
|
|
||||||
set(_python_pcbuild_platform Win32)
|
|
||||||
set(_python_layout_arch win32)
|
|
||||||
set(_python_pcbuild_output_dir win32)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# pybind11 undefines _DEBUG around Python.h so a debug build links the
|
|
||||||
# release python3xx.lib; Py_DEBUG could not load release plugin modules.
|
|
||||||
set(_python_pcbuild_config Release)
|
|
||||||
|
|
||||||
# CPython's PCbuild needs a 64-bit-hosted toolchain: find_msbuild.bat picks the
|
|
||||||
# 32-bit Bin\MSBuild.exe, whose x86 cl.exe/link.exe run out of address space
|
|
||||||
# (fatal C1002 "out of heap space") building the LTCG-optimized pythoncore.
|
|
||||||
# That 32-bit MSBuild ignores PreferredToolArchitecture, so build.bat must be
|
|
||||||
# pointed at a 64-bit MSBuild via the MSBUILD env var. The native arm64 MSBuild
|
|
||||||
# then hosts arm64 tools on its own; the amd64 MSBuild still defaults to x86, so
|
|
||||||
# PreferredToolArchitecture pins it to x64. Scoped to this build step, so
|
|
||||||
# CPython's own sources stay untouched.
|
|
||||||
set(_python_env_args "GIT_CEILING_DIRECTORIES=<SOURCE_DIR>/..")
|
|
||||||
if(CMAKE_GENERATOR_INSTANCE) # empty for non-VS generators (e.g. Ninja)
|
|
||||||
set(_python_msbuild "${CMAKE_GENERATOR_INSTANCE}/MSBuild/Current/Bin/${_python_msbuild_host}/MSBuild.exe")
|
|
||||||
if(EXISTS "${_python_msbuild}")
|
|
||||||
file(TO_NATIVE_PATH "${_python_msbuild}" _python_msbuild_native)
|
|
||||||
list(APPEND _python_env_args "MSBUILD=${_python_msbuild_native}")
|
|
||||||
else()
|
|
||||||
# Loud signal: a silent fall-through to the 32-bit MSBuild reintroduces C1002.
|
|
||||||
message(WARNING "Bundled Python: 64-bit MSBuild not found at '${_python_msbuild}'. "
|
|
||||||
"CPython will fall back to find_msbuild.bat's default (32-bit) MSBuild, which may "
|
|
||||||
"fail with C1002 (out of heap space) building the optimized pythoncore.")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if(_python_tool_arch)
|
|
||||||
list(APPEND _python_env_args "PreferredToolArchitecture=${_python_tool_arch}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# MSBuild reads extra switches from PCbuild/msbuild.rsp.
|
|
||||||
set(_python_rsp "/p:PlatformToolset=${_python_platform_toolset}\n")
|
|
||||||
# VS 2026's ARM64 code generator needs about 27 GB for one function in
|
|
||||||
# Objects/unicodectype.c (python/cpython#153668); the property sheet compiles
|
|
||||||
# that file without optimisation.
|
|
||||||
if(_python_pcbuild_platform STREQUAL "ARM64")
|
|
||||||
file(TO_NATIVE_PATH "${CMAKE_CURRENT_LIST_DIR}/arm64-unicodectype.props" _python_arm64_props)
|
|
||||||
string(APPEND _python_rsp "/p:ForceImportAfterCppTargets=\"${_python_arm64_props}\"\n")
|
|
||||||
endif()
|
|
||||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/python3-msbuild.rsp" "${_python_rsp}")
|
|
||||||
set(_conf_cmd
|
|
||||||
${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_BINARY_DIR}/python3-msbuild.rsp" <SOURCE_DIR>/PCbuild/msbuild.rsp
|
|
||||||
)
|
|
||||||
set(_build_cmd
|
|
||||||
${CMAKE_COMMAND} -E env ${_python_env_args}
|
|
||||||
cmd /c PCbuild\\build.bat
|
|
||||||
-p ${_python_pcbuild_platform}
|
|
||||||
-c ${_python_pcbuild_config}
|
|
||||||
--no-tkinter
|
|
||||||
)
|
|
||||||
set(_install_cmd
|
|
||||||
${CMAKE_COMMAND}
|
|
||||||
-DPYTHON_SOURCE_DIR=<SOURCE_DIR>
|
|
||||||
-DPYTHON_BUILD_DIR=<SOURCE_DIR>/PCbuild/${_python_pcbuild_output_dir}
|
|
||||||
-DPYTHON_DEST_DIR=${DESTDIR}/libpython
|
|
||||||
-DPYTHON_LAYOUT_ARCH=${_python_layout_arch}
|
|
||||||
-P ${CMAKE_CURRENT_LIST_DIR}/stage_windows.cmake
|
|
||||||
)
|
|
||||||
elseif(APPLE)
|
|
||||||
# macOS configuration
|
|
||||||
if(CMAKE_OSX_ARCHITECTURES)
|
|
||||||
set(_python_target_arch "${CMAKE_OSX_ARCHITECTURES}")
|
|
||||||
else()
|
|
||||||
set(_python_target_arch "${CMAKE_SYSTEM_PROCESSOR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|arm64|aarch64")
|
|
||||||
set(_python_build_arch aarch64)
|
|
||||||
set(_python_build_arch_flag "arm64")
|
|
||||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64")
|
|
||||||
set(_python_build_arch x86_64)
|
|
||||||
set(_python_build_arch_flag "x86_64")
|
|
||||||
else()
|
|
||||||
set(_python_build_arch "${CMAKE_SYSTEM_PROCESSOR}")
|
|
||||||
set(_python_build_arch_flag "${CMAKE_SYSTEM_PROCESSOR}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(_python_target_arch MATCHES "ARM64|arm64|aarch64")
|
|
||||||
set(_python_host_arch aarch64)
|
|
||||||
set(_python_arch_flag "arm64")
|
|
||||||
elseif(_python_target_arch MATCHES "x86_64|AMD64|amd64")
|
|
||||||
set(_python_host_arch x86_64)
|
|
||||||
set(_python_arch_flag "x86_64")
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "Unsupported macOS Python target architecture: ${_python_target_arch}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(_python_arch_flags "-arch ${_python_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
|
||||||
# No -rpath: all other deps are static, so libpython has no shared
|
|
||||||
# dependencies to find there. headerpad reserves load-command space for
|
|
||||||
# the post-install -add_rpath below.
|
|
||||||
set(_python_ldflags "${_python_arch_flags} -Wl,-headerpad_max_install_names")
|
|
||||||
|
|
||||||
if(IS_CROSS_COMPILE)
|
|
||||||
set(_python_build_tgt --build=${_python_build_arch}-apple-darwin --host=${_python_host_arch}-apple-darwin)
|
|
||||||
set(_python_build_arch_flags "-arch ${_python_build_arch_flag} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
|
|
||||||
set(_python_build_ldflags "${_python_build_arch_flags} -Wl,-rpath,${DESTDIR}/lib")
|
|
||||||
set(_python_build_python_dir "<SOURCE_DIR>/build-python-host")
|
|
||||||
set(_python_build_python "${_python_build_python_dir}/python")
|
|
||||||
set(_conf_cmd
|
|
||||||
/bin/sh -c
|
|
||||||
"rm -rf '${_python_build_python_dir}' && \
|
|
||||||
mkdir -p '${_python_build_python_dir}' && \
|
|
||||||
cd '${_python_build_python_dir}' && \
|
|
||||||
env \
|
|
||||||
CC='${CMAKE_C_COMPILER}' \
|
|
||||||
CXX='${CMAKE_CXX_COMPILER}' \
|
|
||||||
CFLAGS='${_python_build_arch_flags}' \
|
|
||||||
CXXFLAGS='${_python_build_arch_flags}' \
|
|
||||||
LDFLAGS='${_python_build_ldflags}' \
|
|
||||||
MACOSX_DEPLOYMENT_TARGET='${CMAKE_OSX_DEPLOYMENT_TARGET}' \
|
|
||||||
../configure \
|
|
||||||
--prefix='${_python_build_python_dir}/install' \
|
|
||||||
--enable-shared \
|
|
||||||
--without-static-libpython \
|
|
||||||
--disable-test-modules \
|
|
||||||
--build=${_python_build_arch}-apple-darwin && \
|
|
||||||
make -j${NPROC} python && \
|
|
||||||
cd '<SOURCE_DIR>' && \
|
|
||||||
env \
|
|
||||||
CC='${CMAKE_C_COMPILER}' \
|
|
||||||
CXX='${CMAKE_CXX_COMPILER}' \
|
|
||||||
CFLAGS='${_python_arch_flags}' \
|
|
||||||
CXXFLAGS='${_python_arch_flags}' \
|
|
||||||
LDFLAGS='${_python_ldflags}' \
|
|
||||||
MACOSX_DEPLOYMENT_TARGET='${CMAKE_OSX_DEPLOYMENT_TARGET}' \
|
|
||||||
./configure \
|
|
||||||
--prefix='${DESTDIR}/libpython' \
|
|
||||||
--enable-shared \
|
|
||||||
--enable-optimizations \
|
|
||||||
--without-static-libpython \
|
|
||||||
--with-openssl='${DESTDIR}' \
|
|
||||||
--disable-test-modules \
|
|
||||||
${_python_build_tgt} \
|
|
||||||
--with-build-python='${_python_build_python}' \
|
|
||||||
py_cv_module__tkinter=n/a"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
set(_python_build_tgt --build=${_python_host_arch}-apple-darwin)
|
|
||||||
set(_conf_cmd
|
|
||||||
env
|
|
||||||
"CC=${CMAKE_C_COMPILER}"
|
|
||||||
"CXX=${CMAKE_CXX_COMPILER}"
|
|
||||||
"CFLAGS=${_python_arch_flags}"
|
|
||||||
"CXXFLAGS=${_python_arch_flags}"
|
|
||||||
"LDFLAGS=${_python_ldflags}"
|
|
||||||
"MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}"
|
|
||||||
./configure
|
|
||||||
--prefix=${DESTDIR}/libpython
|
|
||||||
--enable-shared
|
|
||||||
--enable-optimizations
|
|
||||||
--without-static-libpython
|
|
||||||
--with-openssl=${DESTDIR}
|
|
||||||
--disable-test-modules
|
|
||||||
${_python_build_tgt}
|
|
||||||
# Tcl/Tk 9.0 (e.g. from Homebrew) is incompatible with CPython 3.12's
|
|
||||||
# _tkinter; OrcaSlicer's embedded Python does not need tkinter anyway.
|
|
||||||
py_cv_module__tkinter=n/a
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
set(_build_cmd make -j${NPROC})
|
|
||||||
|
|
||||||
# CPython stamps libpython with an absolute install name ($prefix/lib/...),
|
|
||||||
# which every consumer inherits at link time and which only exists on the
|
|
||||||
# build host. Normalize once here, before anything links against the dep:
|
|
||||||
# give the dylib an @rpath id and teach the interpreter to find it relative
|
|
||||||
# to itself. Consumers then just need an rpath entry (src/CMakeLists.txt).
|
|
||||||
# install_name_tool invalidates code signatures, so re-sign ad-hoc; CI
|
|
||||||
# re-signs the whole bundle with the real identity later.
|
|
||||||
# ld collapses '//' in -install_name (but not in -rpath) strings, while
|
|
||||||
# ${DESTDIR} ends with a slash -- collapse slashes so -change matches the
|
|
||||||
# recorded install name.
|
|
||||||
string(REGEX REPLACE "/+" "/" _python_prefix "${DESTDIR}/libpython")
|
|
||||||
set(_python_dylib "${_python_prefix}/lib/libpython${_python_version_short}.dylib")
|
|
||||||
set(_python_bin "${_python_prefix}/bin/python${_python_version_short}")
|
|
||||||
set(_install_cmd make install
|
|
||||||
COMMAND install_name_tool -id "@rpath/libpython${_python_version_short}.dylib" "${_python_dylib}"
|
|
||||||
COMMAND install_name_tool -change "${_python_dylib}" "@rpath/libpython${_python_version_short}.dylib" "${_python_bin}"
|
|
||||||
COMMAND install_name_tool -add_rpath "@loader_path/../lib" "${_python_bin}"
|
|
||||||
COMMAND codesign --force --sign - "${_python_dylib}"
|
|
||||||
COMMAND codesign --force --sign - "${_python_bin}"
|
|
||||||
)
|
|
||||||
else()
|
|
||||||
# Linux/Unix
|
|
||||||
# Kept verbatim, no slash normalization (unlike the macOS branch's
|
|
||||||
# collapsed copy): the LDFLAGS rpath below is recorded byte-for-byte in
|
|
||||||
# the ELF, and the OLD_RPATH handed to relocate_linux.cmake must match it
|
|
||||||
# exactly -- both derive from this one variable to make that structural.
|
|
||||||
set(_python_prefix "${DESTDIR}/libpython")
|
|
||||||
# The rpath points at libpython's real install dir, so the interpreter runs
|
|
||||||
# in-tree pre-relocation -- and, critically, it reserves enough RUNPATH
|
|
||||||
# bytes for the in-place $ORIGIN rewrite at install time (Flatpak's
|
|
||||||
# DESTDIR is the short /app) -- see relocate_linux.cmake.
|
|
||||||
set(_conf_cmd ./configure
|
|
||||||
--prefix=${_python_prefix}
|
|
||||||
--enable-shared
|
|
||||||
--enable-optimizations
|
|
||||||
--with-openssl=${DESTDIR}
|
|
||||||
--without-static-libpython
|
|
||||||
--disable-test-modules
|
|
||||||
# Tcl/Tk 9.0 is incompatible with CPython 3.12's _tkinter; not needed here.
|
|
||||||
py_cv_module__tkinter=n/a
|
|
||||||
LDFLAGS=-Wl,-rpath,${_python_prefix}/lib
|
|
||||||
)
|
|
||||||
set(_build_cmd make -j${NPROC})
|
|
||||||
set(_install_cmd make install
|
|
||||||
COMMAND ${CMAKE_COMMAND}
|
|
||||||
"-DPYTHON_BIN=${_python_prefix}/bin/python${_python_version_short}"
|
|
||||||
"-DOLD_RPATH=${_python_prefix}/lib"
|
|
||||||
-P "${CMAKE_CURRENT_LIST_DIR}/relocate_linux.cmake"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
ExternalProject_Add(dep_python3
|
|
||||||
URL "${_python_url}"
|
|
||||||
URL_HASH SHA256=${_python_sha256}
|
|
||||||
PATCH_COMMAND ${_patch_cmd}
|
|
||||||
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/python3
|
|
||||||
BUILD_IN_SOURCE ON
|
|
||||||
CONFIGURE_COMMAND ${_conf_cmd}
|
|
||||||
BUILD_COMMAND ${_build_cmd}
|
|
||||||
INSTALL_COMMAND ${_install_cmd}
|
|
||||||
)
|
|
||||||
|
|
||||||
# Python depends on OpenSSL and ZLIB
|
|
||||||
if(TARGET dep_OpenSSL)
|
|
||||||
add_dependencies(dep_python3 dep_OpenSSL)
|
|
||||||
endif()
|
|
||||||
if(TARGET dep_ZLIB)
|
|
||||||
add_dependencies(dep_python3 dep_ZLIB)
|
|
||||||
endif()
|
|
||||||
Vendored
-10
@@ -1,10 +0,0 @@
|
|||||||
# Repoint the installed interpreter's RUNPATH from the absolute deps dir to a
|
|
||||||
# self-relative entry so the bundled runtime is relocatable (the deps tree,
|
|
||||||
# Flatpak /app/libpython, and AppImage $APPDIR/lib/python all keep bin/ and
|
|
||||||
# lib/ as siblings). $ORIGIN is expanded by the dynamic loader; CMake leaves
|
|
||||||
# it alone (only ${...} is expanded here). RPATH_CHANGE edits the ELF in
|
|
||||||
# place, so the new entry must not be longer than the old one: the reserved
|
|
||||||
# ${DESTDIR}/libpython/lib is at least 18 bytes even for the shortest
|
|
||||||
# supported DESTDIR (Flatpak's /app), longer than the 14-byte $ORIGIN/../lib.
|
|
||||||
# Invoked from python3.cmake with -DPYTHON_BIN=... -DOLD_RPATH=...
|
|
||||||
file(RPATH_CHANGE FILE "${PYTHON_BIN}" OLD_RPATH "${OLD_RPATH}" NEW_RPATH "$ORIGIN/../lib")
|
|
||||||
Vendored
-58
@@ -1,58 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
|
|
||||||
set(_python_abi "312")
|
|
||||||
|
|
||||||
foreach(_var PYTHON_SOURCE_DIR PYTHON_BUILD_DIR PYTHON_DEST_DIR PYTHON_LAYOUT_ARCH)
|
|
||||||
if(NOT DEFINED ${_var} OR "${${_var}}" STREQUAL "")
|
|
||||||
message(FATAL_ERROR "${_var} is required")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
set(_python_exe "${PYTHON_BUILD_DIR}/python.exe")
|
|
||||||
|
|
||||||
if(NOT EXISTS "${_python_exe}")
|
|
||||||
message(FATAL_ERROR "Built Python executable not found: ${_python_exe}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
file(REMOVE_RECURSE "${PYTHON_DEST_DIR}")
|
|
||||||
file(MAKE_DIRECTORY "${PYTHON_DEST_DIR}")
|
|
||||||
|
|
||||||
# CPython's Windows layout helper reads LICENSE.txt from the build output.
|
|
||||||
# Source archives ship this file as LICENSE, so provide the expected name.
|
|
||||||
if(EXISTS "${PYTHON_SOURCE_DIR}/LICENSE" AND NOT EXISTS "${PYTHON_BUILD_DIR}/LICENSE.txt")
|
|
||||||
configure_file("${PYTHON_SOURCE_DIR}/LICENSE" "${PYTHON_BUILD_DIR}/LICENSE.txt" COPYONLY)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
execute_process(
|
|
||||||
COMMAND
|
|
||||||
"${CMAKE_COMMAND}" -E env
|
|
||||||
"PYTHONHOME="
|
|
||||||
"PYTHONPATH=${PYTHON_SOURCE_DIR}/Lib"
|
|
||||||
"${_python_exe}"
|
|
||||||
"${PYTHON_SOURCE_DIR}/PC/layout"
|
|
||||||
--source "${PYTHON_SOURCE_DIR}"
|
|
||||||
--build "${PYTHON_BUILD_DIR}"
|
|
||||||
--arch "${PYTHON_LAYOUT_ARCH}"
|
|
||||||
--copy "${PYTHON_DEST_DIR}"
|
|
||||||
--include-dev
|
|
||||||
WORKING_DIRECTORY "${PYTHON_SOURCE_DIR}"
|
|
||||||
RESULT_VARIABLE _layout_result
|
|
||||||
)
|
|
||||||
|
|
||||||
if(NOT _layout_result EQUAL 0)
|
|
||||||
message(FATAL_ERROR "CPython Windows layout staging failed with exit code ${_layout_result}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(_required_files
|
|
||||||
"${PYTHON_DEST_DIR}/Lib/encodings/__init__.py"
|
|
||||||
"${PYTHON_DEST_DIR}/include/Python.h"
|
|
||||||
"${PYTHON_DEST_DIR}/python.exe"
|
|
||||||
"${PYTHON_DEST_DIR}/python${_python_abi}.dll"
|
|
||||||
"${PYTHON_DEST_DIR}/libs/python${_python_abi}.lib"
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach(_required_file IN LISTS _required_files)
|
|
||||||
if(NOT EXISTS "${_required_file}")
|
|
||||||
message(FATAL_ERROR "Staged Python file missing: ${_required_file}")
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
Vendored
-37
@@ -1,37 +0,0 @@
|
|||||||
# wxInspector finds wxWidgets through CMake's FindwxWidgets module, which only
|
|
||||||
# searches lib/vc*_lib because _WX_TOOL is hardcoded to "vc". A superbuild driven
|
|
||||||
# by clang-cl installs wxWidgets into lib/clang_x64_lib, so hand the module the
|
|
||||||
# directory wxWidgets actually used, derived the same way wxWidgetsConfig.cmake
|
|
||||||
# derives it.
|
|
||||||
set(_wxinspector_wx_hints "")
|
|
||||||
if (MSVC)
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
|
||||||
set(_wx_compiler_prefix "clang")
|
|
||||||
else ()
|
|
||||||
set(_wx_compiler_prefix "vc")
|
|
||||||
endif ()
|
|
||||||
set(_wx_arch_suffix "")
|
|
||||||
if (CMAKE_GENERATOR_PLATFORM AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
|
|
||||||
string(TOLOWER "_${CMAKE_GENERATOR_PLATFORM}" _wx_arch_suffix)
|
|
||||||
elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|
||||||
set(_wx_arch_suffix "_x64")
|
|
||||||
endif ()
|
|
||||||
set(_wxinspector_wx_hints
|
|
||||||
"-DwxWidgets_ROOT_DIR=${DESTDIR}"
|
|
||||||
"-DwxWidgets_LIB_DIR=${DESTDIR}/lib/${_wx_compiler_prefix}${_wx_arch_suffix}_lib")
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(
|
|
||||||
wxInspector
|
|
||||||
URL https://github.com/Noisyfox/wxInspector/archive/refs/tags/v1.0.0.zip
|
|
||||||
URL_HASH SHA256=0ba163956f2d468b19a91b96c5aba66ee9610843ea41dda628ea44cdafde7db7
|
|
||||||
DEPENDS ${WXWIDGETS_PKG}
|
|
||||||
CMAKE_ARGS
|
|
||||||
-DCMAKE_CXX_FLAGS="-DwxDEBUG_LEVEL=0"
|
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
|
||||||
${_wxinspector_wx_hints}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
add_debug_dep(dep_wxInspector)
|
|
||||||
endif ()
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
diff --git a/src/osx/cocoa/colour.mm b/src/osx/cocoa/colour.mm
|
|
||||||
index 31515d146f..86b33e94a2 100644
|
|
||||||
--- a/src/osx/cocoa/colour.mm
|
|
||||||
+++ b/src/osx/cocoa/colour.mm
|
|
||||||
@@ -125,3 +125,3 @@
|
|
||||||
wxOSXEffectiveAppearanceSetter helper;
|
|
||||||
- if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
|
|
||||||
+ if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpace:[NSColorSpace sRGBColorSpace]] )
|
|
||||||
return [colRGBA redComponent];
|
|
||||||
@@ -134,3 +134,3 @@
|
|
||||||
wxOSXEffectiveAppearanceSetter helper;
|
|
||||||
- if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
|
|
||||||
+ if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpace:[NSColorSpace sRGBColorSpace]] )
|
|
||||||
return [colRGBA greenComponent];
|
|
||||||
@@ -143,3 +143,3 @@
|
|
||||||
wxOSXEffectiveAppearanceSetter helper;
|
|
||||||
- if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
|
|
||||||
+ if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpace:[NSColorSpace sRGBColorSpace]] )
|
|
||||||
return [colRGBA blueComponent];
|
|
||||||
@@ -152,3 +152,3 @@
|
|
||||||
wxOSXEffectiveAppearanceSetter helper;
|
|
||||||
- if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] )
|
|
||||||
+ if ( NSColor* colRGBA = [m_nsColour colorUsingColorSpace:[NSColorSpace sRGBColorSpace]] )
|
|
||||||
return [colRGBA alphaComponent];
|
|
||||||
@@ -160,3 +160,3 @@
|
|
||||||
{
|
|
||||||
- return [m_nsColour colorUsingColorSpaceName:NSCalibratedRGBColorSpace] != nil;
|
|
||||||
+ return [m_nsColour colorUsingColorSpace:[NSColorSpace sRGBColorSpace]] != nil;
|
|
||||||
}
|
|
||||||
Vendored
-11
@@ -21,22 +21,11 @@ else ()
|
|||||||
set(_wx_edge "-DwxUSE_WEBVIEW_EDGE=OFF")
|
set(_wx_edge "-DwxUSE_WEBVIEW_EDGE=OFF")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
set(_wx_patch_command "")
|
|
||||||
if (APPLE)
|
|
||||||
set(_wx_patch_command
|
|
||||||
${GIT_EXECUTABLE} checkout -f -- src/osx/cocoa/colour.mm
|
|
||||||
COMMAND ${GIT_EXECUTABLE} apply --verbose
|
|
||||||
${CMAKE_CURRENT_LIST_DIR}/0001-macos-use-srgb-colour-components.patch
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(
|
orcaslicer_add_cmake_project(
|
||||||
wxWidgets
|
wxWidgets
|
||||||
GIT_REPOSITORY "https://github.com/SoftFever/Orca-deps-wxWidgets"
|
GIT_REPOSITORY "https://github.com/SoftFever/Orca-deps-wxWidgets"
|
||||||
GIT_TAG v3.3.2
|
GIT_TAG v3.3.2
|
||||||
GIT_SHALLOW ON
|
GIT_SHALLOW ON
|
||||||
GIT_SUBMODULES 3rdparty/catch 3rdparty/pcre 3rdparty/libwebp
|
|
||||||
PATCH_COMMAND ${_wx_patch_command}
|
|
||||||
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} ${JPEG_PKG}
|
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} ${JPEG_PKG}
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
-DwxBUILD_PRECOMP=ON
|
-DwxBUILD_PRECOMP=ON
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ add_subdirectory(libigl)
|
|||||||
add_subdirectory(libnest2d)
|
add_subdirectory(libnest2d)
|
||||||
add_subdirectory(mcut)
|
add_subdirectory(mcut)
|
||||||
add_subdirectory(md4c)
|
add_subdirectory(md4c)
|
||||||
add_subdirectory(mdns)
|
|
||||||
add_subdirectory(miniz)
|
add_subdirectory(miniz)
|
||||||
add_subdirectory(minilzo)
|
add_subdirectory(minilzo)
|
||||||
add_subdirectory(qhull)
|
add_subdirectory(qhull)
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first, Impor
|
|||||||
rewind(fp);
|
rewind(fp);
|
||||||
try{
|
try{
|
||||||
char solid_name[256];
|
char solid_name[256];
|
||||||
int res_solid = fscanf(fp, " solid %255[^\n]", solid_name);
|
int res_solid = fscanf(fp, " solid %[^\n]", solid_name);
|
||||||
if (res_solid == 1) {
|
if (res_solid == 1) {
|
||||||
char* mw_position = strstr(solid_name, "MW");
|
char* mw_position = strstr(solid_name, "MW");
|
||||||
if (mw_position != NULL) {
|
if (mw_position != NULL) {
|
||||||
@@ -170,7 +170,7 @@ static bool stl_read(stl_file *stl, FILE *fp, int first_facet, bool first, Impor
|
|||||||
char version_str[16];
|
char version_str[16];
|
||||||
char model_id_str[128];
|
char model_id_str[128];
|
||||||
char country_code_str[16];
|
char country_code_str[16];
|
||||||
int num_values = sscanf(mw_position + 3, "%15s %127s %15s", version_str, model_id_str, country_code_str);
|
int num_values = sscanf(mw_position + 3, "%s %s %s", version_str, model_id_str, country_code_str);
|
||||||
if (num_values == 3) {
|
if (num_values == 3) {
|
||||||
if (strcmp(version_str, "1.0") == 0) {
|
if (strcmp(version_str, "1.0") == 0) {
|
||||||
model_id = model_id_str;
|
model_id = model_id_str;
|
||||||
|
|||||||
@@ -37,11 +37,7 @@ target_include_directories(Clipper2
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
if (MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
target_compile_options(Clipper2 PRIVATE /W4 /WX)
|
||||||
target_compile_options(Clipper2 PRIVATE /W4 /WX)
|
|
||||||
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
|
||||||
target_compile_options(Clipper2 PRIVATE /W4)
|
|
||||||
endif()
|
|
||||||
else()
|
else()
|
||||||
target_compile_options(Clipper2 PRIVATE -Wall -Wextra -Wpedantic -Werror)
|
target_compile_options(Clipper2 PRIVATE -Wall -Wextra -Wpedantic -Werror)
|
||||||
target_link_libraries(Clipper2 PUBLIC -lm)
|
target_link_libraries(Clipper2 PUBLIC -lm)
|
||||||
|
|||||||
@@ -2856,7 +2856,6 @@ const ImWchar* ImFontAtlas::GetGlyphRangesDefault()
|
|||||||
{
|
{
|
||||||
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
0x0020, 0x00FF, // Basic Latin + Latin Supplement
|
||||||
0x2000, 0x206F, // General Punctuation
|
0x2000, 0x206F, // General Punctuation
|
||||||
0x2103, 0x2103, // ℃ Celsius symbol
|
|
||||||
0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
|
0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
|
||||||
0x31F0, 0x31FF, // Katakana Phonetic Extensions
|
0x31F0, 0x31FF, // Katakana Phonetic Extensions
|
||||||
0xFF00, 0xFFEF, // Half-width characters
|
0xFF00, 0xFFEF, // Half-width characters
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ void ImGui::Text(const char* fmt, ...)
|
|||||||
void ImGui::TextCentered(const char* text, ...)
|
void ImGui::TextCentered(const char* text, ...)
|
||||||
{
|
{
|
||||||
va_list vaList;
|
va_list vaList;
|
||||||
va_start(vaList, text);
|
va_start(vaList,&text);
|
||||||
|
|
||||||
float font_size = ImGui::GetFontSize() * strlen(text) / 2;
|
float font_size = ImGui::GetFontSize() * strlen(text) / 2;
|
||||||
ImGui::SameLine(ImGui::GetCursorPos().x / 2 - font_size + (font_size / 2));
|
ImGui::SameLine(ImGui::GetCursorPos().x / 2 - font_size + (font_size / 2));
|
||||||
|
|||||||
@@ -465,57 +465,6 @@ static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *stat
|
|||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
||||||
y = r.ymin;
|
y = r.ymin;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// In multi-line mode, clamp y to stay within the text vertical bounds.
|
|
||||||
// This lets the click still land at a valid location if the mouse is slightly
|
|
||||||
// above or below the text.
|
|
||||||
StbTexteditRow r;
|
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
|
||||||
int i = 0;
|
|
||||||
float base_y = 0, y_min, y_max;
|
|
||||||
|
|
||||||
// Get the first row to establish y_min and start the iteration
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
|
||||||
if (r.num_chars <= 0)
|
|
||||||
{
|
|
||||||
state->cursor = 0;
|
|
||||||
state->select_start = state->cursor;
|
|
||||||
state->select_end = state->cursor;
|
|
||||||
state->has_preferred_x = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
y_min = r.ymin;
|
|
||||||
y_max = base_y + r.ymax;
|
|
||||||
i = r.num_chars;
|
|
||||||
base_y += r.baseline_y_delta;
|
|
||||||
|
|
||||||
// Walk the remaining rows to find the bottom of the last row
|
|
||||||
while (i < n)
|
|
||||||
{
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
|
||||||
if (r.num_chars <= 0)
|
|
||||||
break;
|
|
||||||
y_max = base_y + r.ymax;
|
|
||||||
i += r.num_chars;
|
|
||||||
base_y += r.baseline_y_delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the text ends with a newline, account for the empty trailing line
|
|
||||||
// so the cursor can be placed on it
|
|
||||||
if (n > 0 && STB_TEXTEDIT_GETCHAR(str, n - 1) == STB_TEXTEDIT_NEWLINE)
|
|
||||||
{
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, n);
|
|
||||||
y_max = base_y + r.ymax;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract half the last line height to avoid rounding issues when the mouse
|
|
||||||
// is just barely below the last line (keep cursor on the last line, not after the text)
|
|
||||||
y_max -= (r.ymax - r.ymin) * 0.5f;
|
|
||||||
|
|
||||||
if (y < y_min) y = y_min;
|
|
||||||
if (y > y_max) y = y_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
state->cursor = stb_text_locate_coord(str, x, y);
|
state->cursor = stb_text_locate_coord(str, x, y);
|
||||||
state->select_start = state->cursor;
|
state->select_start = state->cursor;
|
||||||
@@ -536,50 +485,6 @@ static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state
|
|||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
||||||
y = r.ymin;
|
y = r.ymin;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
// In multi-line mode, clamp y to stay within the text vertical bounds.
|
|
||||||
// This lets the drag keep working if the mouse goes off the top or bottom of the text.
|
|
||||||
StbTexteditRow r;
|
|
||||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
|
||||||
int i = 0;
|
|
||||||
float base_y = 0, y_min, y_max;
|
|
||||||
|
|
||||||
// Get the first row to establish y_min and start the iteration
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, 0);
|
|
||||||
if (r.num_chars <= 0)
|
|
||||||
return;
|
|
||||||
y_min = r.ymin;
|
|
||||||
y_max = base_y + r.ymax;
|
|
||||||
i = r.num_chars;
|
|
||||||
base_y += r.baseline_y_delta;
|
|
||||||
|
|
||||||
// Walk the remaining rows to find the bottom of the last row
|
|
||||||
while (i < n)
|
|
||||||
{
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, i);
|
|
||||||
if (r.num_chars <= 0)
|
|
||||||
break;
|
|
||||||
y_max = base_y + r.ymax;
|
|
||||||
i += r.num_chars;
|
|
||||||
base_y += r.baseline_y_delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the text ends with a newline, account for the empty trailing line
|
|
||||||
// so the cursor can be placed on it
|
|
||||||
if (n > 0 && STB_TEXTEDIT_GETCHAR(str, n - 1) == STB_TEXTEDIT_NEWLINE)
|
|
||||||
{
|
|
||||||
STB_TEXTEDIT_LAYOUTROW(&r, str, n);
|
|
||||||
y_max = base_y + r.ymax;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract half the last line height to avoid rounding issues when the mouse
|
|
||||||
// is just barely below the last line (keep cursor on the last line, not after the text)
|
|
||||||
y_max -= (r.ymax - r.ymin) * 0.5f;
|
|
||||||
|
|
||||||
if (y < y_min) y = y_min;
|
|
||||||
if (y > y_max) y = y_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state->select_start == state->select_end)
|
if (state->select_start == state->select_end)
|
||||||
state->select_start = state->cursor;
|
state->select_start = state->cursor;
|
||||||
|
|||||||
@@ -1123,17 +1123,18 @@ private:
|
|||||||
|
|
||||||
std::vector<RawShape> objs,excludes;
|
std::vector<RawShape> objs,excludes;
|
||||||
for (const Item &item : items_) {
|
for (const Item &item : items_) {
|
||||||
if (item.isFixed())
|
if (item.isFixed()) continue;
|
||||||
excludes.push_back(item.transformedShape());
|
objs.push_back(item.transformedShape());
|
||||||
else
|
|
||||||
objs.push_back(item.transformedShape());
|
|
||||||
}
|
}
|
||||||
if (objs.empty())
|
if (objs.empty())
|
||||||
return;
|
return;
|
||||||
// Without fixed items this inner-fit NFP can exceed clipper's range and crash MSVC.
|
|
||||||
if (!excludes.empty())
|
|
||||||
{ // find a best position inside NFP of fixed items (excluded regions), so the center of pile is cloest to bed center
|
{ // find a best position inside NFP of fixed items (excluded regions), so the center of pile is cloest to bed center
|
||||||
RawShape objs_convex_hull = sl::convexHull(objs);
|
RawShape objs_convex_hull = sl::convexHull(objs);
|
||||||
|
for (const Item &item : items_) {
|
||||||
|
if (item.isFixed()) {
|
||||||
|
excludes.push_back(item.transformedShape());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto nfps = calcnfp(objs_convex_hull, excludes, bbin, Lvl<MaxNfpLevel::value>());
|
auto nfps = calcnfp(objs_convex_hull, excludes, bbin, Lvl<MaxNfpLevel::value>());
|
||||||
if (nfps.empty()) {
|
if (nfps.empty()) {
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.13)
|
|
||||||
|
|
||||||
project(mdns)
|
|
||||||
|
|
||||||
# Static library wrapping mjansson/mdns (public domain) plus the cxmdns C++
|
|
||||||
# wrapper from CrealityPrint v7.1.1 (AGPL-3.0). See NOTICE.md for attribution.
|
|
||||||
add_library(mdns STATIC
|
|
||||||
mdns.h
|
|
||||||
mdns.c
|
|
||||||
cxmdns.h
|
|
||||||
cxmdns.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(mdns SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
|
|
||||||
if (MSVC)
|
|
||||||
# mjansson/mdns uses GetAdaptersAddresses (Iphlpapi) and Winsock2 (Ws2_32).
|
|
||||||
target_link_libraries(mdns PUBLIC Iphlpapi Ws2_32)
|
|
||||||
endif()
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# mDNS / DNS-SD library
|
|
||||||
|
|
||||||
The four files in this directory implement mDNS / DNS-SD lookup and are
|
|
||||||
vendored from third-party sources:
|
|
||||||
|
|
||||||
## mdns.h, mdns.c
|
|
||||||
|
|
||||||
mDNS / DNS-SD lookup library by Mattias Jansson. Originally released to
|
|
||||||
the public domain at https://github.com/mjansson/mdns.
|
|
||||||
|
|
||||||
The exact files here were taken from CrealityOfficial/CrealityPrint
|
|
||||||
v7.1.1, which split the upstream header-only library into separate
|
|
||||||
declaration (mdns.h) and implementation (mdns.c) files.
|
|
||||||
|
|
||||||
- Source: https://github.com/mjansson/mdns
|
|
||||||
- License: Public domain (no restrictions on use)
|
|
||||||
|
|
||||||
## cxmdns.h, cxmdns.cpp
|
|
||||||
|
|
||||||
Thin C++ wrapper over mdns.{h,c} that exposes a single function:
|
|
||||||
|
|
||||||
std::vector<machine_info> syncDiscoveryService(
|
|
||||||
const std::vector<std::string>& prefix);
|
|
||||||
|
|
||||||
It sends a DNS-SD meta-discovery query (`_services._dns-sd._udp.local.`),
|
|
||||||
listens for ~5 seconds, and returns `{ip, service_name}` for every
|
|
||||||
service announcement whose name contains any of the given prefixes.
|
|
||||||
|
|
||||||
OrcaSlicer uses this to find Creality K-series printers on the LAN
|
|
||||||
(service-name prefix "Creality"), since K-series firmware announces
|
|
||||||
each printer under a per-device-unique service type
|
|
||||||
`_Creality-<MAC-derived-hex>._udp.local.` that no fixed-name query can
|
|
||||||
target.
|
|
||||||
|
|
||||||
- Source: CrealityOfficial/CrealityPrint v7.1.1
|
|
||||||
`src/slic3r/GUI/print_manage/utils/cxmdns.{h,cpp}`
|
|
||||||
- License: GNU AGPL-3.0 (compatible with OrcaSlicer's AGPL-3.0; see
|
|
||||||
top-level LICENSE.txt)
|
|
||||||
- Imported: 2026-05-19
|
|
||||||
@@ -1,256 +0,0 @@
|
|||||||
#include"cxmdns.h"
|
|
||||||
#include"mdns.h"
|
|
||||||
#ifdef _WIN32
|
|
||||||
#define _CRT_SECURE_NO_WARNINGS 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include<string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <signal.h>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <winsock2.h>
|
|
||||||
#include <iphlpapi.h>
|
|
||||||
#define sleep(x) Sleep(x * 1000)
|
|
||||||
#else
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <ifaddrs.h>
|
|
||||||
#include <net/if.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Alias some things to simulate recieving data to fuzz library
|
|
||||||
#if defined(MDNS_FUZZING)
|
|
||||||
#define recvfrom(sock, buffer, capacity, flags, src_addr, addrlen) ((mdns_ssize_t)capacity)
|
|
||||||
#define printf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "mdns.h"
|
|
||||||
|
|
||||||
#if defined(MDNS_FUZZING)
|
|
||||||
#undef recvfrom
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace cxnet
|
|
||||||
{
|
|
||||||
template <typename F>
|
|
||||||
mdns_record_callback_fn lambda2function(F lambda)
|
|
||||||
{
|
|
||||||
static auto lambdabak = lambda;
|
|
||||||
return [](int sock, const struct sockaddr* from, size_t addrlen,
|
|
||||||
mdns_entry_type_t entry, uint16_t query_id, uint16_t rtype,
|
|
||||||
uint16_t rclass, uint32_t ttl, const void* data, size_t size,
|
|
||||||
size_t name_offset, size_t name_length, size_t record_offset,
|
|
||||||
size_t record_length, void* user_data)->int {lambdabak(sock, from, addrlen, entry, query_id, rtype, rclass, ttl, data, size, name_offset, name_length, record_offset, record_length, user_data); return 0; };
|
|
||||||
}
|
|
||||||
|
|
||||||
volatile sig_atomic_t running = 1;
|
|
||||||
#ifdef _WIN32
|
|
||||||
BOOL console_handler(DWORD signal) {
|
|
||||||
if (signal == CTRL_C_EVENT) {
|
|
||||||
running = 0;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void signal_handler(int signal) {
|
|
||||||
running = 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void recvMachineInfoFromSocket(int sock, void* buffer, size_t capacity, const std::vector<std::string>& prefix, std::vector<machine_info>& retmachineInfos, int recIndex)
|
|
||||||
{
|
|
||||||
struct sockaddr_in6 addr;
|
|
||||||
struct sockaddr* saddr = (struct sockaddr*)&addr;
|
|
||||||
socklen_t addrlen = sizeof(addr);
|
|
||||||
memset(&addr, 0, sizeof(addr));
|
|
||||||
#ifdef __APPLE__
|
|
||||||
saddr->sa_len = sizeof(addr);
|
|
||||||
#endif
|
|
||||||
mdns_ssize_t ret = recvfrom(sock, (char*)buffer, (mdns_size_t)capacity, 0, saddr, &addrlen);
|
|
||||||
if (ret <= 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
size_t data_size = (size_t)ret;
|
|
||||||
//size_t records = 0;
|
|
||||||
const uint16_t* data = (uint16_t*)buffer;
|
|
||||||
|
|
||||||
uint16_t query_id = mdns_ntohs(data++);
|
|
||||||
uint16_t flags = mdns_ntohs(data++);
|
|
||||||
uint16_t questions = mdns_ntohs(data++);
|
|
||||||
uint16_t answer_rrs = mdns_ntohs(data++);
|
|
||||||
uint16_t authority_rrs = mdns_ntohs(data++);
|
|
||||||
uint16_t additional_rrs = mdns_ntohs(data++);
|
|
||||||
|
|
||||||
// According to RFC 6762 the query ID MUST match the sent query ID (which is 0 in our case)
|
|
||||||
if (query_id || (flags != 0x8400))
|
|
||||||
return; // Not a reply to our question
|
|
||||||
|
|
||||||
// It seems some implementations do not fill the correct questions field,
|
|
||||||
// so ignore this check for now and only validate answer string
|
|
||||||
// if (questions != 1)
|
|
||||||
// return 0;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < questions; ++i) {
|
|
||||||
size_t offset = MDNS_POINTER_DIFF(data, buffer);
|
|
||||||
size_t verify_offset = 12;
|
|
||||||
// Verify it's our question, _services._dns-sd._udp.local.
|
|
||||||
if (!mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
|
|
||||||
sizeof(mdns_services_query), &verify_offset))
|
|
||||||
return;
|
|
||||||
data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset);
|
|
||||||
|
|
||||||
uint16_t rtype = mdns_ntohs(data++);
|
|
||||||
uint16_t rclass = mdns_ntohs(data++);
|
|
||||||
|
|
||||||
// Make sure we get a reply based on our PTR question for class IN
|
|
||||||
if ((rtype != MDNS_RECORDTYPE_PTR) || ((rclass & 0x7FFF) != MDNS_CLASS_IN))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < answer_rrs; ++i) {
|
|
||||||
size_t offset = MDNS_POINTER_DIFF(data, buffer);
|
|
||||||
size_t verify_offset = 12;
|
|
||||||
// Verify it's an answer to our question, _services._dns-sd._udp.local.
|
|
||||||
size_t name_offset = offset;
|
|
||||||
int is_answer = mdns_string_equal(buffer, data_size, &offset, mdns_services_query,
|
|
||||||
sizeof(mdns_services_query), &verify_offset);
|
|
||||||
if (!is_answer && !mdns_string_skip(buffer, data_size, &offset))
|
|
||||||
break;
|
|
||||||
size_t name_length = offset - name_offset;
|
|
||||||
if ((offset + 10) > data_size)
|
|
||||||
return;
|
|
||||||
data = (const uint16_t*)MDNS_POINTER_OFFSET(buffer, offset);
|
|
||||||
|
|
||||||
uint16_t rtype = mdns_ntohs(data++);
|
|
||||||
uint16_t rclass = mdns_ntohs(data++);
|
|
||||||
uint32_t ttl = mdns_ntohl(data);
|
|
||||||
data += 2;
|
|
||||||
uint16_t length = mdns_ntohs(data++);
|
|
||||||
if (length > (data_size - offset))
|
|
||||||
return;
|
|
||||||
|
|
||||||
static char addrbuf[64];
|
|
||||||
static char entrybuf[256];
|
|
||||||
static char namebuf[256];
|
|
||||||
|
|
||||||
if (is_answer) {
|
|
||||||
offset = MDNS_POINTER_DIFF(data, buffer);
|
|
||||||
(void)sizeof(sock);
|
|
||||||
(void)sizeof(query_id);
|
|
||||||
(void)sizeof(name_length);
|
|
||||||
//(void)sizeof(0);
|
|
||||||
mdns_string_t fromaddrstr = ip_address_to_string(addrbuf, sizeof(addrbuf), saddr, addrlen);
|
|
||||||
mdns_string_t entrystr =
|
|
||||||
mdns_string_extract(buffer, data_size, &name_offset, entrybuf, sizeof(entrybuf));
|
|
||||||
if (rtype == MDNS_RECORDTYPE_PTR) {
|
|
||||||
mdns_string_t namestr = mdns_record_parse_ptr(buffer, data_size, offset, length,
|
|
||||||
namebuf, sizeof(namebuf));
|
|
||||||
if (!namestr.str || namestr.length == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const std::string answer_name(namestr.str, namestr.length);
|
|
||||||
bool bFound = false;
|
|
||||||
for (const auto& item : prefix)
|
|
||||||
{
|
|
||||||
if (answer_name.find(item) != std::string::npos)
|
|
||||||
bFound = true;
|
|
||||||
}
|
|
||||||
if (!bFound)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
char ip[16] = { 0 };
|
|
||||||
sscanf(fromaddrstr.str, "%[^:]", ip);
|
|
||||||
retmachineInfos.push_back({ ip, answer_name });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<machine_info> syncDiscoveryService(const std::vector<std::string>& prefix)
|
|
||||||
{
|
|
||||||
std::vector<machine_info> retmachineInfos;
|
|
||||||
const char* hostname = "cxslice-host";
|
|
||||||
// Initialize network environment
|
|
||||||
#ifdef _WIN32
|
|
||||||
WORD versionWanted = MAKEWORD(1, 1);
|
|
||||||
WSADATA wsaData;
|
|
||||||
if (WSAStartup(versionWanted, &wsaData)) {
|
|
||||||
printf("Failed to initialize WinSock\n");
|
|
||||||
return retmachineInfos;
|
|
||||||
}
|
|
||||||
char hostname_buffer[256];
|
|
||||||
DWORD hostname_size = (DWORD)sizeof(hostname_buffer);
|
|
||||||
if (GetComputerNameA(hostname_buffer, &hostname_size))
|
|
||||||
hostname = hostname_buffer;
|
|
||||||
SetConsoleCtrlHandler(console_handler, TRUE);
|
|
||||||
#else
|
|
||||||
char hostname_buffer[256];
|
|
||||||
size_t hostname_size = sizeof(hostname_buffer);
|
|
||||||
if (gethostname(hostname_buffer, hostname_size) == 0)
|
|
||||||
hostname = hostname_buffer;
|
|
||||||
signal(SIGINT, signal_handler);
|
|
||||||
#endif
|
|
||||||
int sockets[32];
|
|
||||||
int num_sockets = open_client_sockets(sockets, sizeof(sockets) / sizeof(sockets[0]), 0);
|
|
||||||
if (num_sockets <= 0) {
|
|
||||||
printf("Failed to open any client sockets\n");
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSACleanup();
|
|
||||||
#endif
|
|
||||||
return retmachineInfos;
|
|
||||||
}
|
|
||||||
printf("Opened %d socket%s for DNS-SD\n", num_sockets, num_sockets > 1 ? "s" : "");
|
|
||||||
printf("Sending DNS-SD discovery\n");
|
|
||||||
for (int isock = 0; isock < num_sockets; ++isock) {
|
|
||||||
if (mdns_discovery_send(sockets[isock]))
|
|
||||||
printf("Failed to send DNS-DS discovery: %s\n", strerror(errno));
|
|
||||||
}
|
|
||||||
size_t capacity = 2048;
|
|
||||||
void* buffer = malloc(capacity);
|
|
||||||
size_t recordNum = 0;
|
|
||||||
void* user_data = 0;
|
|
||||||
|
|
||||||
// This is a simple implementation that loops for 5 seconds or as long as we get replies
|
|
||||||
int res;
|
|
||||||
printf("Reading DNS-SD replies\n");
|
|
||||||
do {
|
|
||||||
struct timeval timeout;
|
|
||||||
timeout.tv_sec = 5;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
|
|
||||||
int nfds = 0;
|
|
||||||
fd_set readfs;
|
|
||||||
FD_ZERO(&readfs);
|
|
||||||
for (int isock = 0; isock < num_sockets; ++isock) {
|
|
||||||
if (sockets[isock] >= nfds)
|
|
||||||
nfds = sockets[isock] + 1;
|
|
||||||
FD_SET(sockets[isock], &readfs);
|
|
||||||
}
|
|
||||||
res = select(nfds, &readfs, 0, 0, &timeout);
|
|
||||||
if (res > 0) {
|
|
||||||
for (int isock = 0; isock < num_sockets; ++isock) {
|
|
||||||
if (FD_ISSET(sockets[isock], &readfs)) {
|
|
||||||
// records += mdns_discovery_recv(sockets[isock], buffer, capacity, query_callback,
|
|
||||||
// 0);
|
|
||||||
recvMachineInfoFromSocket(sockets[isock], buffer, capacity, prefix, retmachineInfos, isock);
|
|
||||||
recordNum++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} while (res > 0);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
//
|
|
||||||
for (int isock = 0; isock < num_sockets; ++isock)
|
|
||||||
mdns_socket_close(sockets[isock]);
|
|
||||||
printf("Closed socket%s\n", num_sockets ? "s" : "");
|
|
||||||
#ifdef _WIN32
|
|
||||||
WSACleanup();
|
|
||||||
#endif
|
|
||||||
return std::move(retmachineInfos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#ifndef _CX_MDNS_H
|
|
||||||
#define _CX_MDNS_H
|
|
||||||
#include<string>
|
|
||||||
#include<vector>
|
|
||||||
|
|
||||||
namespace cxnet
|
|
||||||
{
|
|
||||||
struct machine_info
|
|
||||||
{
|
|
||||||
std::string machineIp;
|
|
||||||
std::string answer;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<machine_info> syncDiscoveryService(const std::vector<std::string>& prefix);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -11,8 +11,6 @@ add_library(miniz_static STATIC
|
|||||||
|
|
||||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
|
if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU")
|
||||||
target_compile_definitions(miniz_static PRIVATE _GNU_SOURCE)
|
target_compile_definitions(miniz_static PRIVATE _GNU_SOURCE)
|
||||||
elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
|
||||||
target_compile_options(miniz_static PRIVATE /clang:-Wno-error=incompatible-pointer-types)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
target_link_libraries(miniz INTERFACE miniz_static)
|
target_link_libraries(miniz INTERFACE miniz_static)
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>, All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its contributors
|
|
||||||
may be used to endorse or promote products derived from this software
|
|
||||||
without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of
|
|
||||||
external contributions to this project including patches, pull requests, etc.
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
.. figure:: https://github.com/pybind/pybind11/raw/master/docs/pybind11-logo.png
|
|
||||||
:alt: pybind11 logo
|
|
||||||
|
|
||||||
**pybind11 (v3) — Seamless interoperability between C++ and Python**
|
|
||||||
|
|
||||||
|Latest Documentation Status| |Stable Documentation Status| |Gitter chat| |GitHub Discussions|
|
|
||||||
|
|
||||||
|CI| |Build status| |SPEC 4 — Using and Creating Nightly Wheels|
|
|
||||||
|
|
||||||
|Repology| |PyPI package| |Conda-forge| |Python Versions|
|
|
||||||
|
|
||||||
`Setuptools example <https://github.com/pybind/python_example>`_
|
|
||||||
• `Scikit-build example <https://github.com/pybind/scikit_build_example>`_
|
|
||||||
• `CMake example <https://github.com/pybind/cmake_example>`_
|
|
||||||
|
|
||||||
.. start
|
|
||||||
|
|
||||||
|
|
||||||
**pybind11** is a lightweight header-only library that exposes C++ types
|
|
||||||
in Python and vice versa, mainly to create Python bindings of existing
|
|
||||||
C++ code. Its goals and syntax are similar to the excellent
|
|
||||||
`Boost.Python <http://www.boost.org/doc/libs/1_58_0/libs/python/doc/>`_
|
|
||||||
library by David Abrahams: to minimize boilerplate code in traditional
|
|
||||||
extension modules by inferring type information using compile-time
|
|
||||||
introspection.
|
|
||||||
|
|
||||||
The main issue with Boost.Python—and the reason for creating such a
|
|
||||||
similar project—is Boost. Boost is an enormously large and complex suite
|
|
||||||
of utility libraries that works with almost every C++ compiler in
|
|
||||||
existence. This compatibility has its cost: arcane template tricks and
|
|
||||||
workarounds are necessary to support the oldest and buggiest of compiler
|
|
||||||
specimens. Now that C++11-compatible compilers are widely available,
|
|
||||||
this heavy machinery has become an excessively large and unnecessary
|
|
||||||
dependency.
|
|
||||||
|
|
||||||
Think of this library as a tiny self-contained version of Boost.Python
|
|
||||||
with everything stripped away that isn't relevant for binding
|
|
||||||
generation. Without comments, the core header files only require ~4K
|
|
||||||
lines of code and depend on Python (CPython 3.8+, PyPy, or GraalPy) and the C++
|
|
||||||
standard library. This compact implementation was possible thanks to some C++11
|
|
||||||
language features (specifically: tuples, lambda functions and variadic
|
|
||||||
templates). Since its creation, this library has grown beyond Boost.Python in
|
|
||||||
many ways, leading to dramatically simpler binding code in many common
|
|
||||||
situations.
|
|
||||||
|
|
||||||
Tutorial and reference documentation is provided at
|
|
||||||
`pybind11.readthedocs.io <https://pybind11.readthedocs.io/en/latest>`_.
|
|
||||||
A PDF version of the manual is available
|
|
||||||
`here <https://pybind11.readthedocs.io/_/downloads/en/latest/pdf/>`_.
|
|
||||||
And the source code is always available at
|
|
||||||
`github.com/pybind/pybind11 <https://github.com/pybind/pybind11>`_.
|
|
||||||
|
|
||||||
|
|
||||||
Core features
|
|
||||||
-------------
|
|
||||||
|
|
||||||
|
|
||||||
pybind11 can map the following core C++ features to Python:
|
|
||||||
|
|
||||||
- Functions accepting and returning custom data structures per value,
|
|
||||||
reference, or pointer
|
|
||||||
- Instance methods and static methods
|
|
||||||
- Overloaded functions
|
|
||||||
- Instance attributes and static attributes
|
|
||||||
- Arbitrary exception types
|
|
||||||
- Enumerations
|
|
||||||
- Callbacks
|
|
||||||
- Iterators and ranges
|
|
||||||
- Custom operators
|
|
||||||
- Single and multiple inheritance
|
|
||||||
- STL data structures
|
|
||||||
- Smart pointers with reference counting like ``std::shared_ptr``
|
|
||||||
- Internal references with correct reference counting
|
|
||||||
- C++ classes with virtual (and pure virtual) methods can be extended
|
|
||||||
in Python
|
|
||||||
- Integrated NumPy support (NumPy 2 requires pybind11 2.12+)
|
|
||||||
|
|
||||||
Goodies
|
|
||||||
-------
|
|
||||||
|
|
||||||
In addition to the core functionality, pybind11 provides some extra
|
|
||||||
goodies:
|
|
||||||
|
|
||||||
- CPython 3.8+, PyPy3 7.3.17+, and GraalPy 24.1+ are supported with an
|
|
||||||
implementation-agnostic interface (see older versions for older CPython
|
|
||||||
and PyPy versions).
|
|
||||||
|
|
||||||
- It is possible to bind C++11 lambda functions with captured
|
|
||||||
variables. The lambda capture data is stored inside the resulting
|
|
||||||
Python function object.
|
|
||||||
|
|
||||||
- pybind11 uses C++11 move constructors and move assignment operators
|
|
||||||
whenever possible to efficiently transfer custom data types.
|
|
||||||
|
|
||||||
- It's easy to expose the internal storage of custom data types through
|
|
||||||
Pythons' buffer protocols. This is handy e.g. for fast conversion
|
|
||||||
between C++ matrix classes like Eigen and NumPy without expensive
|
|
||||||
copy operations.
|
|
||||||
|
|
||||||
- pybind11 can automatically vectorize functions so that they are
|
|
||||||
transparently applied to all entries of one or more NumPy array
|
|
||||||
arguments.
|
|
||||||
|
|
||||||
- Python's slice-based access and assignment operations can be
|
|
||||||
supported with just a few lines of code.
|
|
||||||
|
|
||||||
- Everything is contained in just a few header files; there is no need
|
|
||||||
to link against any additional libraries.
|
|
||||||
|
|
||||||
- Binaries are generally smaller by a factor of at least 2 compared to
|
|
||||||
equivalent bindings generated by Boost.Python. A recent pybind11
|
|
||||||
conversion of PyRosetta, an enormous Boost.Python binding project,
|
|
||||||
`reported <https://graylab.jhu.edu/Sergey/2016.RosettaCon/PyRosetta-4.pdf>`_
|
|
||||||
a binary size reduction of **5.4x** and compile time reduction by
|
|
||||||
**5.8x**.
|
|
||||||
|
|
||||||
- Function signatures are precomputed at compile time (using
|
|
||||||
``constexpr``), leading to smaller binaries.
|
|
||||||
|
|
||||||
- With little extra effort, C++ types can be pickled and unpickled
|
|
||||||
similar to regular Python objects.
|
|
||||||
|
|
||||||
Supported compilers
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
1. Clang/LLVM 3.3 or newer (for Apple Xcode's clang, this is 5.0.0 or
|
|
||||||
newer)
|
|
||||||
2. GCC 4.8 or newer
|
|
||||||
3. Microsoft Visual Studio 2022 or newer (2019 probably works, but was dropped in CI)
|
|
||||||
4. Intel classic C++ compiler 18 or newer (ICC 20.2 tested in CI)
|
|
||||||
5. Cygwin/GCC (previously tested on 2.5.1)
|
|
||||||
6. NVCC (CUDA 11.0 tested in CI)
|
|
||||||
7. NVIDIA PGI (20.9 tested in CI)
|
|
||||||
|
|
||||||
Supported Platforms
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
* Windows, Linux, macOS, and iOS
|
|
||||||
* CPython 3.8+, Pyodide, PyPy, and GraalPy
|
|
||||||
* C++11, C++14, C++17, C++20, and C++23
|
|
||||||
|
|
||||||
About
|
|
||||||
-----
|
|
||||||
|
|
||||||
This project was created by `Wenzel
|
|
||||||
Jakob <http://rgl.epfl.ch/people/wjakob>`_. Significant features and/or
|
|
||||||
improvements to the code were contributed by
|
|
||||||
Jonas Adler,
|
|
||||||
Lori A. Burns,
|
|
||||||
Sylvain Corlay,
|
|
||||||
Eric Cousineau,
|
|
||||||
Aaron Gokaslan,
|
|
||||||
Ralf Grosse-Kunstleve,
|
|
||||||
Trent Houliston,
|
|
||||||
Axel Huebl,
|
|
||||||
@hulucc,
|
|
||||||
Yannick Jadoul,
|
|
||||||
Sergey Lyskov,
|
|
||||||
Johan Mabille,
|
|
||||||
Tomasz Miąsko,
|
|
||||||
Dean Moldovan,
|
|
||||||
Ben Pritchard,
|
|
||||||
Jason Rhinelander,
|
|
||||||
Boris Schäling,
|
|
||||||
Pim Schellart,
|
|
||||||
Henry Schreiner,
|
|
||||||
Ivan Smirnov,
|
|
||||||
Dustin Spicuzza,
|
|
||||||
Boris Staletic,
|
|
||||||
Ethan Steinberg,
|
|
||||||
Patrick Stewart,
|
|
||||||
Ivor Wanders,
|
|
||||||
and
|
|
||||||
Xiaofei Wang.
|
|
||||||
|
|
||||||
We thank Google for a generous financial contribution to the continuous
|
|
||||||
integration infrastructure used by this project.
|
|
||||||
|
|
||||||
|
|
||||||
Contributing
|
|
||||||
~~~~~~~~~~~~
|
|
||||||
|
|
||||||
See the `contributing
|
|
||||||
guide <https://github.com/pybind/pybind11/blob/master/.github/CONTRIBUTING.md>`_
|
|
||||||
for information on building and contributing to pybind11.
|
|
||||||
|
|
||||||
License
|
|
||||||
~~~~~~~
|
|
||||||
|
|
||||||
pybind11 is provided under a BSD-style license that can be found in the
|
|
||||||
`LICENSE <https://github.com/pybind/pybind11/blob/master/LICENSE>`_
|
|
||||||
file. By using, distributing, or contributing to this project, you agree
|
|
||||||
to the terms and conditions of this license.
|
|
||||||
|
|
||||||
.. |Latest Documentation Status| image:: https://readthedocs.org/projects/pybind11/badge?version=latest
|
|
||||||
:target: http://pybind11.readthedocs.org/en/latest
|
|
||||||
.. |Stable Documentation Status| image:: https://img.shields.io/badge/docs-stable-blue.svg
|
|
||||||
:target: http://pybind11.readthedocs.org/en/stable
|
|
||||||
.. |Gitter chat| image:: https://img.shields.io/gitter/room/gitterHQ/gitter.svg
|
|
||||||
:target: https://gitter.im/pybind/Lobby
|
|
||||||
.. |CI| image:: https://github.com/pybind/pybind11/workflows/CI/badge.svg
|
|
||||||
:target: https://github.com/pybind/pybind11/actions
|
|
||||||
.. |Build status| image:: https://ci.appveyor.com/api/projects/status/riaj54pn4h08xy40?svg=true
|
|
||||||
:target: https://ci.appveyor.com/project/wjakob/pybind11
|
|
||||||
.. |PyPI package| image:: https://img.shields.io/pypi/v/pybind11.svg
|
|
||||||
:target: https://pypi.org/project/pybind11/
|
|
||||||
.. |Conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pybind11.svg
|
|
||||||
:target: https://github.com/conda-forge/pybind11-feedstock
|
|
||||||
.. |Repology| image:: https://repology.org/badge/latest-versions/python:pybind11.svg
|
|
||||||
:target: https://repology.org/project/python:pybind11/versions
|
|
||||||
.. |Python Versions| image:: https://img.shields.io/pypi/pyversions/pybind11.svg
|
|
||||||
:target: https://pypi.org/project/pybind11/
|
|
||||||
.. |GitHub Discussions| image:: https://img.shields.io/static/v1?label=Discussions&message=Ask&color=blue&logo=github
|
|
||||||
:target: https://github.com/pybind/pybind11/discussions
|
|
||||||
.. |SPEC 4 — Using and Creating Nightly Wheels| image:: https://img.shields.io/badge/SPEC-4-green?labelColor=%23004811&color=%235CA038
|
|
||||||
:target: https://scientific-python.org/specs/spec-0004/
|
|
||||||
@@ -1,722 +0,0 @@
|
|||||||
/*
|
|
||||||
pybind11/attr.h: Infrastructure for processing custom
|
|
||||||
type and function attributes
|
|
||||||
|
|
||||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
|
||||||
|
|
||||||
All rights reserved. Use of this source code is governed by a
|
|
||||||
BSD-style license that can be found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "detail/common.h"
|
|
||||||
#include "cast.h"
|
|
||||||
#include "trampoline_self_life_support.h"
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|
||||||
|
|
||||||
/// \addtogroup annotations
|
|
||||||
/// @{
|
|
||||||
|
|
||||||
/// Annotation for methods
|
|
||||||
struct is_method {
|
|
||||||
handle class_;
|
|
||||||
explicit is_method(const handle &c) : class_(c) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Annotation for setters
|
|
||||||
struct is_setter {};
|
|
||||||
|
|
||||||
/// Annotation for operators
|
|
||||||
struct is_operator {};
|
|
||||||
|
|
||||||
/// Annotation for classes that cannot be subclassed
|
|
||||||
struct is_final {};
|
|
||||||
|
|
||||||
/// Annotation for parent scope
|
|
||||||
struct scope {
|
|
||||||
handle value;
|
|
||||||
explicit scope(const handle &s) : value(s) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Annotation for documentation
|
|
||||||
struct doc {
|
|
||||||
const char *value;
|
|
||||||
explicit doc(const char *value) : value(value) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Annotation for function names
|
|
||||||
struct name {
|
|
||||||
const char *value;
|
|
||||||
explicit name(const char *value) : value(value) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Annotation indicating that a function is an overload associated with a given "sibling"
|
|
||||||
struct sibling {
|
|
||||||
handle value;
|
|
||||||
explicit sibling(const handle &value) : value(value.ptr()) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Annotation indicating that a class derives from another given type
|
|
||||||
template <typename T>
|
|
||||||
struct base {
|
|
||||||
|
|
||||||
PYBIND11_DEPRECATED(
|
|
||||||
"base<T>() was deprecated in favor of specifying 'T' as a template argument to class_")
|
|
||||||
base() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Keep patient alive while nurse lives
|
|
||||||
template <size_t Nurse, size_t Patient>
|
|
||||||
struct keep_alive {};
|
|
||||||
|
|
||||||
/// Annotation indicating that a class is involved in a multiple inheritance relationship
|
|
||||||
struct multiple_inheritance {};
|
|
||||||
|
|
||||||
/// Annotation which enables dynamic attributes, i.e. adds `__dict__` to a class
|
|
||||||
struct dynamic_attr {};
|
|
||||||
|
|
||||||
/// Annotation which enables the buffer protocol for a type
|
|
||||||
struct buffer_protocol {};
|
|
||||||
|
|
||||||
/// Annotation which enables releasing the GIL before calling the C++ destructor of wrapped
|
|
||||||
/// instances (pybind/pybind11#1446).
|
|
||||||
struct release_gil_before_calling_cpp_dtor {};
|
|
||||||
|
|
||||||
/// Annotation which requests that a special metaclass is created for a type
|
|
||||||
struct metaclass {
|
|
||||||
handle value;
|
|
||||||
|
|
||||||
PYBIND11_DEPRECATED("py::metaclass() is no longer required. It's turned on by default now.")
|
|
||||||
metaclass() = default;
|
|
||||||
|
|
||||||
/// Override pybind11's default metaclass
|
|
||||||
explicit metaclass(handle value) : value(value) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Specifies a custom callback with signature `void (PyHeapTypeObject*)` that
|
|
||||||
/// may be used to customize the Python type.
|
|
||||||
///
|
|
||||||
/// The callback is invoked immediately before `PyType_Ready`.
|
|
||||||
///
|
|
||||||
/// Note: This is an advanced interface, and uses of it may require changes to
|
|
||||||
/// work with later versions of pybind11. You may wish to consult the
|
|
||||||
/// implementation of `make_new_python_type` in `detail/classes.h` to understand
|
|
||||||
/// the context in which the callback will be run.
|
|
||||||
struct custom_type_setup {
|
|
||||||
using callback = std::function<void(PyHeapTypeObject *heap_type)>;
|
|
||||||
|
|
||||||
explicit custom_type_setup(callback value) : value(std::move(value)) {}
|
|
||||||
|
|
||||||
callback value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Annotation that marks a class as local to the module:
|
|
||||||
struct module_local {
|
|
||||||
const bool value;
|
|
||||||
constexpr explicit module_local(bool v = true) : value(v) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Annotation to mark enums as an arithmetic type
|
|
||||||
struct arithmetic {};
|
|
||||||
|
|
||||||
/// Mark a function for addition at the beginning of the existing overload chain instead of the end
|
|
||||||
struct prepend {};
|
|
||||||
|
|
||||||
/** \rst
|
|
||||||
A call policy which places one or more guard variables (``Ts...``) around the function call.
|
|
||||||
|
|
||||||
For example, this definition:
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
|
||||||
|
|
||||||
m.def("foo", foo, py::call_guard<T>());
|
|
||||||
|
|
||||||
is equivalent to the following pseudocode:
|
|
||||||
|
|
||||||
.. code-block:: cpp
|
|
||||||
|
|
||||||
m.def("foo", [](args...) {
|
|
||||||
T scope_guard;
|
|
||||||
return foo(args...); // forwarded arguments
|
|
||||||
});
|
|
||||||
\endrst */
|
|
||||||
template <typename... Ts>
|
|
||||||
struct call_guard;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct call_guard<> {
|
|
||||||
using type = detail::void_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct call_guard<T> {
|
|
||||||
static_assert(std::is_default_constructible<T>::value,
|
|
||||||
"The guard type must be default constructible");
|
|
||||||
|
|
||||||
using type = T;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename... Ts>
|
|
||||||
struct call_guard<T, Ts...> {
|
|
||||||
struct type {
|
|
||||||
T guard{}; // Compose multiple guard types with left-to-right default-constructor order
|
|
||||||
typename call_guard<Ts...>::type next{};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/// @} annotations
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
|
||||||
/* Forward declarations */
|
|
||||||
enum op_id : int;
|
|
||||||
enum op_type : int;
|
|
||||||
struct undefined_t;
|
|
||||||
template <op_id id, op_type ot, typename L = undefined_t, typename R = undefined_t>
|
|
||||||
struct op_;
|
|
||||||
void keep_alive_impl(size_t Nurse, size_t Patient, function_call &call, handle ret);
|
|
||||||
|
|
||||||
/// Internal data structure which holds metadata about a keyword argument
|
|
||||||
struct argument_record {
|
|
||||||
const char *name; ///< Argument name
|
|
||||||
const char *descr; ///< Human-readable version of the argument value
|
|
||||||
handle value; ///< Associated Python object
|
|
||||||
bool convert : 1; ///< True if the argument is allowed to convert when loading
|
|
||||||
bool none : 1; ///< True if None is allowed when loading
|
|
||||||
|
|
||||||
argument_record(const char *name, const char *descr, handle value, bool convert, bool none)
|
|
||||||
: name(name), descr(descr), value(value), convert(convert), none(none) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Internal data structure which holds metadata about a bound function (signature, overloads,
|
|
||||||
/// etc.)
|
|
||||||
#define PYBIND11_DETAIL_FUNCTION_RECORD_ABI_ID "v1" // PLEASE UPDATE if the struct is changed.
|
|
||||||
struct function_record {
|
|
||||||
function_record()
|
|
||||||
: is_constructor(false), is_new_style_constructor(false), is_stateless(false),
|
|
||||||
is_operator(false), is_method(false), is_setter(false), has_args(false),
|
|
||||||
has_kwargs(false), prepend(false) {}
|
|
||||||
|
|
||||||
/// Function name
|
|
||||||
char *name = nullptr; /* why no C++ strings? They generate heavier code.. */
|
|
||||||
|
|
||||||
// User-specified documentation string
|
|
||||||
char *doc = nullptr;
|
|
||||||
|
|
||||||
/// Human-readable version of the function signature
|
|
||||||
char *signature = nullptr;
|
|
||||||
|
|
||||||
/// List of registered keyword arguments
|
|
||||||
std::vector<argument_record> args;
|
|
||||||
|
|
||||||
/// Pointer to lambda function which converts arguments and performs the actual call
|
|
||||||
handle (*impl)(function_call &) = nullptr;
|
|
||||||
|
|
||||||
/// Storage for the wrapped function pointer and captured data, if any
|
|
||||||
void *data[3] = {};
|
|
||||||
|
|
||||||
/// Pointer to custom destructor for 'data' (if needed)
|
|
||||||
void (*free_data)(function_record *ptr) = nullptr;
|
|
||||||
|
|
||||||
/// Return value policy associated with this function
|
|
||||||
return_value_policy policy = return_value_policy::automatic;
|
|
||||||
|
|
||||||
/// True if name == '__init__'
|
|
||||||
bool is_constructor : 1;
|
|
||||||
|
|
||||||
/// True if this is a new-style `__init__` defined in `detail/init.h`
|
|
||||||
bool is_new_style_constructor : 1;
|
|
||||||
|
|
||||||
/// True if this is a stateless function pointer
|
|
||||||
bool is_stateless : 1;
|
|
||||||
|
|
||||||
/// True if this is an operator (__add__), etc.
|
|
||||||
bool is_operator : 1;
|
|
||||||
|
|
||||||
/// True if this is a method
|
|
||||||
bool is_method : 1;
|
|
||||||
|
|
||||||
/// True if this is a setter
|
|
||||||
bool is_setter : 1;
|
|
||||||
|
|
||||||
/// True if the function has a '*args' argument
|
|
||||||
bool has_args : 1;
|
|
||||||
|
|
||||||
/// True if the function has a '**kwargs' argument
|
|
||||||
bool has_kwargs : 1;
|
|
||||||
|
|
||||||
/// True if this function is to be inserted at the beginning of the overload resolution chain
|
|
||||||
bool prepend : 1;
|
|
||||||
|
|
||||||
/// Number of arguments (including py::args and/or py::kwargs, if present)
|
|
||||||
std::uint16_t nargs;
|
|
||||||
|
|
||||||
/// Number of leading positional arguments, which are terminated by a py::args or py::kwargs
|
|
||||||
/// argument or by a py::kw_only annotation.
|
|
||||||
std::uint16_t nargs_pos = 0;
|
|
||||||
|
|
||||||
/// Number of leading arguments (counted in `nargs`) that are positional-only
|
|
||||||
std::uint16_t nargs_pos_only = 0;
|
|
||||||
|
|
||||||
/// Python method object
|
|
||||||
PyMethodDef *def = nullptr;
|
|
||||||
|
|
||||||
/// Python handle to the parent scope (a class or a module)
|
|
||||||
handle scope;
|
|
||||||
|
|
||||||
/// Python handle to the sibling function representing an overload chain
|
|
||||||
handle sibling;
|
|
||||||
|
|
||||||
/// Pointer to next overload
|
|
||||||
function_record *next = nullptr;
|
|
||||||
};
|
|
||||||
// The main purpose of this macro is to make it easy to pin-point the critically related code
|
|
||||||
// sections.
|
|
||||||
#define PYBIND11_ENSURE_PRECONDITION_FOR_FUNCTIONAL_H_PERFORMANCE_OPTIMIZATIONS(...) \
|
|
||||||
static_assert( \
|
|
||||||
__VA_ARGS__, \
|
|
||||||
"Violation of precondition for pybind11/functional.h performance optimizations!")
|
|
||||||
|
|
||||||
/// Special data structure which (temporarily) holds metadata about a bound class
|
|
||||||
struct type_record {
|
|
||||||
PYBIND11_NOINLINE type_record()
|
|
||||||
: multiple_inheritance(false), dynamic_attr(false), buffer_protocol(false),
|
|
||||||
module_local(false), is_final(false), release_gil_before_calling_cpp_dtor(false) {}
|
|
||||||
|
|
||||||
/// Handle to the parent scope
|
|
||||||
handle scope;
|
|
||||||
|
|
||||||
/// Name of the class
|
|
||||||
const char *name = nullptr;
|
|
||||||
|
|
||||||
// Pointer to RTTI type_info data structure
|
|
||||||
const std::type_info *type = nullptr;
|
|
||||||
|
|
||||||
/// How large is the underlying C++ type?
|
|
||||||
size_t type_size = 0;
|
|
||||||
|
|
||||||
/// What is the alignment of the underlying C++ type?
|
|
||||||
size_t type_align = 0;
|
|
||||||
|
|
||||||
/// How large is the type's holder?
|
|
||||||
size_t holder_size = 0;
|
|
||||||
|
|
||||||
/// The global operator new can be overridden with a class-specific variant
|
|
||||||
void *(*operator_new)(size_t) = nullptr;
|
|
||||||
|
|
||||||
/// Function pointer to class_<..>::init_instance
|
|
||||||
void (*init_instance)(instance *, const void *) = nullptr;
|
|
||||||
|
|
||||||
/// Function pointer to class_<..>::dealloc
|
|
||||||
void (*dealloc)(detail::value_and_holder &) = nullptr;
|
|
||||||
|
|
||||||
/// Function pointer for casting alias class (aka trampoline) pointer to
|
|
||||||
/// trampoline_self_life_support pointer. Sidesteps cross-DSO RTTI issues
|
|
||||||
/// on platforms like macOS (see PR #5728 for details).
|
|
||||||
get_trampoline_self_life_support_fn get_trampoline_self_life_support
|
|
||||||
= [](void *) -> trampoline_self_life_support * { return nullptr; };
|
|
||||||
|
|
||||||
/// List of base classes of the newly created type
|
|
||||||
list bases;
|
|
||||||
|
|
||||||
/// Optional docstring
|
|
||||||
const char *doc = nullptr;
|
|
||||||
|
|
||||||
/// Custom metaclass (optional)
|
|
||||||
handle metaclass;
|
|
||||||
|
|
||||||
/// Custom type setup.
|
|
||||||
custom_type_setup::callback custom_type_setup_callback;
|
|
||||||
|
|
||||||
/// Multiple inheritance marker
|
|
||||||
bool multiple_inheritance : 1;
|
|
||||||
|
|
||||||
/// Does the class manage a __dict__?
|
|
||||||
bool dynamic_attr : 1;
|
|
||||||
|
|
||||||
/// Does the class implement the buffer protocol?
|
|
||||||
bool buffer_protocol : 1;
|
|
||||||
|
|
||||||
/// Is the class definition local to the module shared object?
|
|
||||||
bool module_local : 1;
|
|
||||||
|
|
||||||
/// Is the class inheritable from python classes?
|
|
||||||
bool is_final : 1;
|
|
||||||
|
|
||||||
/// Solves pybind/pybind11#1446
|
|
||||||
bool release_gil_before_calling_cpp_dtor : 1;
|
|
||||||
|
|
||||||
holder_enum_t holder_enum_v = holder_enum_t::undefined;
|
|
||||||
|
|
||||||
PYBIND11_NOINLINE void add_base(const std::type_info &base, void *(*caster)(void *) ) {
|
|
||||||
auto *base_info = detail::get_type_info(base, false);
|
|
||||||
if (!base_info) {
|
|
||||||
std::string tname(base.name());
|
|
||||||
detail::clean_type_id(tname);
|
|
||||||
pybind11_fail("generic_type: type \"" + std::string(name)
|
|
||||||
+ "\" referenced unknown base type \"" + tname + "\"");
|
|
||||||
}
|
|
||||||
|
|
||||||
// SMART_HOLDER_BAKEIN_FOLLOW_ON: Refine holder compatibility checks.
|
|
||||||
bool this_has_unique_ptr_holder = (holder_enum_v == holder_enum_t::std_unique_ptr);
|
|
||||||
bool base_has_unique_ptr_holder
|
|
||||||
= (base_info->holder_enum_v == holder_enum_t::std_unique_ptr);
|
|
||||||
if (this_has_unique_ptr_holder != base_has_unique_ptr_holder) {
|
|
||||||
std::string tname(base.name());
|
|
||||||
detail::clean_type_id(tname);
|
|
||||||
pybind11_fail("generic_type: type \"" + std::string(name) + "\" "
|
|
||||||
+ (this_has_unique_ptr_holder ? "does not have" : "has")
|
|
||||||
+ " a non-default holder type while its base \"" + tname + "\" "
|
|
||||||
+ (base_has_unique_ptr_holder ? "does not" : "does"));
|
|
||||||
}
|
|
||||||
|
|
||||||
bases.append((PyObject *) base_info->type);
|
|
||||||
|
|
||||||
#ifdef PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET
|
|
||||||
dynamic_attr |= base_info->type->tp_dictoffset != 0;
|
|
||||||
#else
|
|
||||||
dynamic_attr |= (base_info->type->tp_flags & Py_TPFLAGS_MANAGED_DICT) != 0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (caster) {
|
|
||||||
base_info->implicit_casts.emplace_back(type, caster);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline function_call::function_call(const function_record &f, handle p) : func(f), parent(p) {
|
|
||||||
args.reserve(f.nargs);
|
|
||||||
args_convert.reserve(f.nargs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tag for a new-style `__init__` defined in `detail/init.h`
|
|
||||||
struct is_new_style_constructor {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Partial template specializations to process custom attributes provided to
|
|
||||||
* cpp_function_ and class_. These are either used to initialize the respective
|
|
||||||
* fields in the type_record and function_record data structures or executed at
|
|
||||||
* runtime to deal with custom call policies (e.g. keep_alive).
|
|
||||||
*/
|
|
||||||
template <typename T, typename SFINAE = void>
|
|
||||||
struct process_attribute;
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct process_attribute_default {
|
|
||||||
/// Default implementation: do nothing
|
|
||||||
static void init(const T &, function_record *) {}
|
|
||||||
static void init(const T &, type_record *) {}
|
|
||||||
static void precall(function_call &) {}
|
|
||||||
static void postcall(function_call &, handle) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute specifying the function's name
|
|
||||||
template <>
|
|
||||||
struct process_attribute<name> : process_attribute_default<name> {
|
|
||||||
static void init(const name &n, function_record *r) { r->name = const_cast<char *>(n.value); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute specifying the function's docstring
|
|
||||||
template <>
|
|
||||||
struct process_attribute<doc> : process_attribute_default<doc> {
|
|
||||||
static void init(const doc &n, function_record *r) { r->doc = const_cast<char *>(n.value); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute specifying the function's docstring (provided as a C-style string)
|
|
||||||
template <>
|
|
||||||
struct process_attribute<const char *> : process_attribute_default<const char *> {
|
|
||||||
static void init(const char *d, function_record *r) { r->doc = const_cast<char *>(d); }
|
|
||||||
static void init(const char *d, type_record *r) { r->doc = d; }
|
|
||||||
};
|
|
||||||
template <>
|
|
||||||
struct process_attribute<char *> : process_attribute<const char *> {};
|
|
||||||
|
|
||||||
/// Process an attribute indicating the function's return value policy
|
|
||||||
template <>
|
|
||||||
struct process_attribute<return_value_policy> : process_attribute_default<return_value_policy> {
|
|
||||||
static void init(const return_value_policy &p, function_record *r) { r->policy = p; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute which indicates that this is an overloaded function associated with a
|
|
||||||
/// given sibling
|
|
||||||
template <>
|
|
||||||
struct process_attribute<sibling> : process_attribute_default<sibling> {
|
|
||||||
static void init(const sibling &s, function_record *r) { r->sibling = s.value; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute which indicates that this function is a method
|
|
||||||
template <>
|
|
||||||
struct process_attribute<is_method> : process_attribute_default<is_method> {
|
|
||||||
static void init(const is_method &s, function_record *r) {
|
|
||||||
r->is_method = true;
|
|
||||||
r->scope = s.class_;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute which indicates that this function is a setter
|
|
||||||
template <>
|
|
||||||
struct process_attribute<is_setter> : process_attribute_default<is_setter> {
|
|
||||||
static void init(const is_setter &, function_record *r) { r->is_setter = true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute which indicates the parent scope of a method
|
|
||||||
template <>
|
|
||||||
struct process_attribute<scope> : process_attribute_default<scope> {
|
|
||||||
static void init(const scope &s, function_record *r) { r->scope = s.value; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an attribute which indicates that this function is an operator
|
|
||||||
template <>
|
|
||||||
struct process_attribute<is_operator> : process_attribute_default<is_operator> {
|
|
||||||
static void init(const is_operator &, function_record *r) { r->is_operator = true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<is_new_style_constructor>
|
|
||||||
: process_attribute_default<is_new_style_constructor> {
|
|
||||||
static void init(const is_new_style_constructor &, function_record *r) {
|
|
||||||
r->is_new_style_constructor = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void check_kw_only_arg(const arg &a, function_record *r) {
|
|
||||||
if (r->args.size() > r->nargs_pos && (!a.name || a.name[0] == '\0')) {
|
|
||||||
pybind11_fail("arg(): cannot specify an unnamed argument after a kw_only() annotation or "
|
|
||||||
"args() argument");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void append_self_arg_if_needed(function_record *r) {
|
|
||||||
if (r->is_method && r->args.empty()) {
|
|
||||||
r->args.emplace_back("self", nullptr, handle(), /*convert=*/true, /*none=*/false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Process a keyword argument attribute (*without* a default value)
|
|
||||||
template <>
|
|
||||||
struct process_attribute<arg> : process_attribute_default<arg> {
|
|
||||||
static void init(const arg &a, function_record *r) {
|
|
||||||
append_self_arg_if_needed(r);
|
|
||||||
r->args.emplace_back(a.name, nullptr, handle(), !a.flag_noconvert, a.flag_none);
|
|
||||||
|
|
||||||
check_kw_only_arg(a, r);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process a keyword argument attribute (*with* a default value)
|
|
||||||
template <>
|
|
||||||
struct process_attribute<arg_v> : process_attribute_default<arg_v> {
|
|
||||||
static void init(const arg_v &a, function_record *r) {
|
|
||||||
if (r->is_method && r->args.empty()) {
|
|
||||||
r->args.emplace_back(
|
|
||||||
"self", /*descr=*/nullptr, /*parent=*/handle(), /*convert=*/true, /*none=*/false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!a.value) {
|
|
||||||
#if defined(PYBIND11_DETAILED_ERROR_MESSAGES)
|
|
||||||
std::string descr("'");
|
|
||||||
if (a.name) {
|
|
||||||
descr += std::string(a.name) + ": ";
|
|
||||||
}
|
|
||||||
descr += a.type + "'";
|
|
||||||
if (r->is_method) {
|
|
||||||
if (r->name) {
|
|
||||||
descr += " in method '" + (std::string) str(r->scope) + "."
|
|
||||||
+ (std::string) r->name + "'";
|
|
||||||
} else {
|
|
||||||
descr += " in method of '" + (std::string) str(r->scope) + "'";
|
|
||||||
}
|
|
||||||
} else if (r->name) {
|
|
||||||
descr += " in function '" + (std::string) r->name + "'";
|
|
||||||
}
|
|
||||||
pybind11_fail("arg(): could not convert default argument " + descr
|
|
||||||
+ " into a Python object (type not registered yet?)");
|
|
||||||
#else
|
|
||||||
pybind11_fail("arg(): could not convert default argument "
|
|
||||||
"into a Python object (type not registered yet?). "
|
|
||||||
"#define PYBIND11_DETAILED_ERROR_MESSAGES or compile in debug mode for "
|
|
||||||
"more information.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
r->args.emplace_back(a.name, a.descr, a.value.inc_ref(), !a.flag_noconvert, a.flag_none);
|
|
||||||
|
|
||||||
check_kw_only_arg(a, r);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process a keyword-only-arguments-follow pseudo argument
|
|
||||||
template <>
|
|
||||||
struct process_attribute<kw_only> : process_attribute_default<kw_only> {
|
|
||||||
static void init(const kw_only &, function_record *r) {
|
|
||||||
append_self_arg_if_needed(r);
|
|
||||||
if (r->has_args && r->nargs_pos != static_cast<std::uint16_t>(r->args.size())) {
|
|
||||||
pybind11_fail("Mismatched args() and kw_only(): they must occur at the same relative "
|
|
||||||
"argument location (or omit kw_only() entirely)");
|
|
||||||
}
|
|
||||||
r->nargs_pos = static_cast<std::uint16_t>(r->args.size());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process a positional-only-argument maker
|
|
||||||
template <>
|
|
||||||
struct process_attribute<pos_only> : process_attribute_default<pos_only> {
|
|
||||||
static void init(const pos_only &, function_record *r) {
|
|
||||||
append_self_arg_if_needed(r);
|
|
||||||
r->nargs_pos_only = static_cast<std::uint16_t>(r->args.size());
|
|
||||||
if (r->nargs_pos_only > r->nargs_pos) {
|
|
||||||
pybind11_fail("pos_only(): cannot follow a py::args() argument");
|
|
||||||
}
|
|
||||||
// It also can't follow a kw_only, but a static_assert in pybind11.h checks that
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process a parent class attribute. Single inheritance only (class_ itself already guarantees
|
|
||||||
/// that)
|
|
||||||
template <typename T>
|
|
||||||
struct process_attribute<T, enable_if_t<is_pyobject<T>::value>>
|
|
||||||
: process_attribute_default<handle> {
|
|
||||||
static void init(const handle &h, type_record *r) { r->bases.append(h); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process a parent class attribute (deprecated, does not support multiple inheritance)
|
|
||||||
template <typename T>
|
|
||||||
struct process_attribute<base<T>> : process_attribute_default<base<T>> {
|
|
||||||
static void init(const base<T> &, type_record *r) { r->add_base(typeid(T), nullptr); }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process a multiple inheritance attribute
|
|
||||||
template <>
|
|
||||||
struct process_attribute<multiple_inheritance> : process_attribute_default<multiple_inheritance> {
|
|
||||||
static void init(const multiple_inheritance &, type_record *r) {
|
|
||||||
r->multiple_inheritance = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<dynamic_attr> : process_attribute_default<dynamic_attr> {
|
|
||||||
static void init(const dynamic_attr &, type_record *r) { r->dynamic_attr = true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<custom_type_setup> {
|
|
||||||
static void init(const custom_type_setup &value, type_record *r) {
|
|
||||||
r->custom_type_setup_callback = value.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<is_final> : process_attribute_default<is_final> {
|
|
||||||
static void init(const is_final &, type_record *r) { r->is_final = true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<buffer_protocol> : process_attribute_default<buffer_protocol> {
|
|
||||||
static void init(const buffer_protocol &, type_record *r) { r->buffer_protocol = true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<metaclass> : process_attribute_default<metaclass> {
|
|
||||||
static void init(const metaclass &m, type_record *r) { r->metaclass = m.value; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<module_local> : process_attribute_default<module_local> {
|
|
||||||
static void init(const module_local &l, type_record *r) { r->module_local = l.value; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct process_attribute<release_gil_before_calling_cpp_dtor>
|
|
||||||
: process_attribute_default<release_gil_before_calling_cpp_dtor> {
|
|
||||||
static void init(const release_gil_before_calling_cpp_dtor &, type_record *r) {
|
|
||||||
r->release_gil_before_calling_cpp_dtor = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process a 'prepend' attribute, putting this at the beginning of the overload chain
|
|
||||||
template <>
|
|
||||||
struct process_attribute<prepend> : process_attribute_default<prepend> {
|
|
||||||
static void init(const prepend &, function_record *r) { r->prepend = true; }
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Process an 'arithmetic' attribute for enums (does nothing here)
|
|
||||||
template <>
|
|
||||||
struct process_attribute<arithmetic> : process_attribute_default<arithmetic> {};
|
|
||||||
|
|
||||||
template <typename... Ts>
|
|
||||||
struct process_attribute<call_guard<Ts...>> : process_attribute_default<call_guard<Ts...>> {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process a keep_alive call policy -- invokes keep_alive_impl during the
|
|
||||||
* pre-call handler if both Nurse, Patient != 0 and use the post-call handler
|
|
||||||
* otherwise
|
|
||||||
*/
|
|
||||||
template <size_t Nurse, size_t Patient>
|
|
||||||
struct process_attribute<keep_alive<Nurse, Patient>>
|
|
||||||
: public process_attribute_default<keep_alive<Nurse, Patient>> {
|
|
||||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
|
||||||
static void precall(function_call &call) {
|
|
||||||
keep_alive_impl(Nurse, Patient, call, handle());
|
|
||||||
}
|
|
||||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N != 0 && P != 0, int> = 0>
|
|
||||||
static void postcall(function_call &, handle) {}
|
|
||||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
|
||||||
static void precall(function_call &) {}
|
|
||||||
template <size_t N = Nurse, size_t P = Patient, enable_if_t<N == 0 || P == 0, int> = 0>
|
|
||||||
static void postcall(function_call &call, handle ret) {
|
|
||||||
keep_alive_impl(Nurse, Patient, call, ret);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Recursively iterate over variadic template arguments
|
|
||||||
template <typename... Args>
|
|
||||||
struct process_attributes {
|
|
||||||
static void init(const Args &...args, function_record *r) {
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
|
||||||
using expander = int[];
|
|
||||||
(void) expander{
|
|
||||||
0, ((void) process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
|
||||||
}
|
|
||||||
static void init(const Args &...args, type_record *r) {
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(r);
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(r);
|
|
||||||
using expander = int[];
|
|
||||||
(void) expander{0,
|
|
||||||
(process_attribute<typename std::decay<Args>::type>::init(args, r), 0)...};
|
|
||||||
}
|
|
||||||
static void precall(function_call &call) {
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call);
|
|
||||||
using expander = int[];
|
|
||||||
(void) expander{0,
|
|
||||||
(process_attribute<typename std::decay<Args>::type>::precall(call), 0)...};
|
|
||||||
}
|
|
||||||
static void postcall(function_call &call, handle fn_ret) {
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(call, fn_ret);
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_GCC_UNUSED_BUT_SET_PARAMETER(fn_ret);
|
|
||||||
using expander = int[];
|
|
||||||
(void) expander{
|
|
||||||
0, (process_attribute<typename std::decay<Args>::type>::postcall(call, fn_ret), 0)...};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
using is_call_guard = is_instantiation<call_guard, T>;
|
|
||||||
|
|
||||||
/// Extract the ``type`` from the first `call_guard` in `Extras...` (or `void_type` if none found)
|
|
||||||
template <typename... Extra>
|
|
||||||
using extract_guard_t = typename exactly_one_t<is_call_guard, call_guard<>, Extra...>::type;
|
|
||||||
|
|
||||||
/// Check the number of named arguments at compile time
|
|
||||||
template <typename... Extra,
|
|
||||||
size_t named = constexpr_sum(std::is_base_of<arg, Extra>::value...),
|
|
||||||
size_t self = constexpr_sum(std::is_same<is_method, Extra>::value...)>
|
|
||||||
constexpr bool expected_num_args(size_t nargs, bool has_args, bool has_kwargs) {
|
|
||||||
PYBIND11_WORKAROUND_INCORRECT_MSVC_C4100(nargs, has_args, has_kwargs);
|
|
||||||
return named == 0 || (self + named + size_t(has_args) + size_t(has_kwargs)) == nargs;
|
|
||||||
}
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
/*
|
|
||||||
pybind11/buffer_info.h: Python buffer object interface
|
|
||||||
|
|
||||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
|
||||||
|
|
||||||
All rights reserved. Use of this source code is governed by a
|
|
||||||
BSD-style license that can be found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "detail/common.h"
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
|
||||||
|
|
||||||
// Default, C-style strides
|
|
||||||
inline std::vector<ssize_t> c_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
|
|
||||||
auto ndim = shape.size();
|
|
||||||
std::vector<ssize_t> strides(ndim, itemsize);
|
|
||||||
if (ndim > 0) {
|
|
||||||
for (size_t i = ndim - 1; i > 0; --i) {
|
|
||||||
strides[i - 1] = strides[i] * shape[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strides;
|
|
||||||
}
|
|
||||||
|
|
||||||
// F-style strides; default when constructing an array_t with `ExtraFlags & f_style`
|
|
||||||
inline std::vector<ssize_t> f_strides(const std::vector<ssize_t> &shape, ssize_t itemsize) {
|
|
||||||
auto ndim = shape.size();
|
|
||||||
std::vector<ssize_t> strides(ndim, itemsize);
|
|
||||||
for (size_t i = 1; i < ndim; ++i) {
|
|
||||||
strides[i] = strides[i - 1] * shape[i - 1];
|
|
||||||
}
|
|
||||||
return strides;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename SFINAE = void>
|
|
||||||
struct compare_buffer_info;
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
|
||||||
|
|
||||||
/// Information record describing a Python buffer object
|
|
||||||
struct buffer_info {
|
|
||||||
void *ptr = nullptr; // Pointer to the underlying storage
|
|
||||||
ssize_t itemsize = 0; // Size of individual items in bytes
|
|
||||||
ssize_t size = 0; // Total number of entries
|
|
||||||
std::string format; // For homogeneous buffers, this should be set to
|
|
||||||
// format_descriptor<T>::format()
|
|
||||||
ssize_t ndim = 0; // Number of dimensions
|
|
||||||
std::vector<ssize_t> shape; // Shape of the tensor (1 entry per dimension)
|
|
||||||
std::vector<ssize_t> strides; // Number of bytes between adjacent entries
|
|
||||||
// (for each per dimension)
|
|
||||||
bool readonly = false; // flag to indicate if the underlying storage may be written to
|
|
||||||
|
|
||||||
buffer_info() = default;
|
|
||||||
|
|
||||||
buffer_info(void *ptr,
|
|
||||||
ssize_t itemsize,
|
|
||||||
const std::string &format,
|
|
||||||
ssize_t ndim,
|
|
||||||
detail::any_container<ssize_t> shape_in,
|
|
||||||
detail::any_container<ssize_t> strides_in,
|
|
||||||
bool readonly = false)
|
|
||||||
: ptr(ptr), itemsize(itemsize), size(1), format(format), ndim(ndim),
|
|
||||||
shape(std::move(shape_in)), strides(std::move(strides_in)), readonly(readonly) {
|
|
||||||
if (ndim != (ssize_t) shape.size() || ndim != (ssize_t) strides.size()) {
|
|
||||||
pybind11_fail("buffer_info: ndim doesn't match shape and/or strides length");
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < (size_t) ndim; ++i) {
|
|
||||||
size *= shape[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
buffer_info(T *ptr,
|
|
||||||
detail::any_container<ssize_t> shape_in,
|
|
||||||
detail::any_container<ssize_t> strides_in,
|
|
||||||
bool readonly = false)
|
|
||||||
: buffer_info(private_ctr_tag(),
|
|
||||||
ptr,
|
|
||||||
sizeof(T),
|
|
||||||
format_descriptor<T>::format(),
|
|
||||||
static_cast<ssize_t>(shape_in->size()),
|
|
||||||
std::move(shape_in),
|
|
||||||
std::move(strides_in),
|
|
||||||
readonly) {}
|
|
||||||
|
|
||||||
buffer_info(void *ptr,
|
|
||||||
ssize_t itemsize,
|
|
||||||
const std::string &format,
|
|
||||||
ssize_t size,
|
|
||||||
bool readonly = false)
|
|
||||||
: buffer_info(ptr, itemsize, format, 1, {size}, {itemsize}, readonly) {}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
buffer_info(T *ptr, ssize_t size, bool readonly = false)
|
|
||||||
: buffer_info(ptr, sizeof(T), format_descriptor<T>::format(), size, readonly) {}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
buffer_info(const T *ptr, ssize_t size, bool readonly = true)
|
|
||||||
: buffer_info(
|
|
||||||
const_cast<T *>(ptr), sizeof(T), format_descriptor<T>::format(), size, readonly) {}
|
|
||||||
|
|
||||||
explicit buffer_info(Py_buffer *view, bool ownview = true)
|
|
||||||
: buffer_info(
|
|
||||||
view->buf,
|
|
||||||
view->itemsize,
|
|
||||||
view->format,
|
|
||||||
view->ndim,
|
|
||||||
{view->shape, view->shape + view->ndim},
|
|
||||||
/* Though buffer::request() requests PyBUF_STRIDES, ctypes objects
|
|
||||||
* ignore this flag and return a view with NULL strides.
|
|
||||||
* When strides are NULL, build them manually. */
|
|
||||||
view->strides
|
|
||||||
? std::vector<ssize_t>(view->strides, view->strides + view->ndim)
|
|
||||||
: detail::c_strides({view->shape, view->shape + view->ndim}, view->itemsize),
|
|
||||||
(view->readonly != 0)) {
|
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
|
||||||
this->m_view = view;
|
|
||||||
// NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer)
|
|
||||||
this->ownview = ownview;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer_info(const buffer_info &) = delete;
|
|
||||||
buffer_info &operator=(const buffer_info &) = delete;
|
|
||||||
|
|
||||||
buffer_info(buffer_info &&other) noexcept { (*this) = std::move(other); }
|
|
||||||
|
|
||||||
buffer_info &operator=(buffer_info &&rhs) noexcept {
|
|
||||||
ptr = rhs.ptr;
|
|
||||||
itemsize = rhs.itemsize;
|
|
||||||
size = rhs.size;
|
|
||||||
format = std::move(rhs.format);
|
|
||||||
ndim = rhs.ndim;
|
|
||||||
shape = std::move(rhs.shape);
|
|
||||||
strides = std::move(rhs.strides);
|
|
||||||
std::swap(m_view, rhs.m_view);
|
|
||||||
std::swap(ownview, rhs.ownview);
|
|
||||||
readonly = rhs.readonly;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
~buffer_info() {
|
|
||||||
if (m_view && ownview) {
|
|
||||||
PyBuffer_Release(m_view);
|
|
||||||
delete m_view;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Py_buffer *view() const { return m_view; }
|
|
||||||
Py_buffer *&view() { return m_view; }
|
|
||||||
|
|
||||||
/* True if the buffer item type is equivalent to `T`. */
|
|
||||||
// To define "equivalent" by example:
|
|
||||||
// `buffer_info::item_type_is_equivalent_to<int>(b)` and
|
|
||||||
// `buffer_info::item_type_is_equivalent_to<long>(b)` may both be true
|
|
||||||
// on some platforms, but `int` and `unsigned` will never be equivalent.
|
|
||||||
// For the ground truth, please inspect `detail::compare_buffer_info<>`.
|
|
||||||
template <typename T>
|
|
||||||
bool item_type_is_equivalent_to() const {
|
|
||||||
return detail::compare_buffer_info<T>::compare(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct private_ctr_tag {};
|
|
||||||
|
|
||||||
buffer_info(private_ctr_tag,
|
|
||||||
void *ptr,
|
|
||||||
ssize_t itemsize,
|
|
||||||
const std::string &format,
|
|
||||||
ssize_t ndim,
|
|
||||||
detail::any_container<ssize_t> &&shape_in,
|
|
||||||
detail::any_container<ssize_t> &&strides_in,
|
|
||||||
bool readonly)
|
|
||||||
: buffer_info(
|
|
||||||
ptr, itemsize, format, ndim, std::move(shape_in), std::move(strides_in), readonly) {}
|
|
||||||
|
|
||||||
Py_buffer *m_view = nullptr;
|
|
||||||
bool ownview = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
|
||||||
|
|
||||||
template <typename T, typename SFINAE>
|
|
||||||
struct compare_buffer_info {
|
|
||||||
static bool compare(const buffer_info &b) {
|
|
||||||
// NOLINTNEXTLINE(bugprone-sizeof-expression) Needed for `PyObject *`
|
|
||||||
return b.format == format_descriptor<T>::format() && b.itemsize == (ssize_t) sizeof(T);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct compare_buffer_info<T, detail::enable_if_t<std::is_integral<T>::value>> {
|
|
||||||
static bool compare(const buffer_info &b) {
|
|
||||||
return (size_t) b.itemsize == sizeof(T)
|
|
||||||
&& (b.format == format_descriptor<T>::value
|
|
||||||
|| ((sizeof(T) == sizeof(long))
|
|
||||||
&& b.format == (std::is_unsigned<T>::value ? "L" : "l"))
|
|
||||||
|| ((sizeof(T) == sizeof(size_t))
|
|
||||||
&& b.format == (std::is_unsigned<T>::value ? "N" : "n")));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,228 +0,0 @@
|
|||||||
/*
|
|
||||||
pybind11/chrono.h: Transparent conversion between std::chrono and python's datetime
|
|
||||||
|
|
||||||
Copyright (c) 2016 Trent Houliston <trent@houliston.me> and
|
|
||||||
Wenzel Jakob <wenzel.jakob@epfl.ch>
|
|
||||||
|
|
||||||
All rights reserved. Use of this source code is governed by a
|
|
||||||
BSD-style license that can be found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pybind11.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <cmath>
|
|
||||||
#include <ctime>
|
|
||||||
#include <datetime.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
|
||||||
|
|
||||||
template <typename type>
|
|
||||||
class duration_caster {
|
|
||||||
public:
|
|
||||||
using rep = typename type::rep;
|
|
||||||
using period = typename type::period;
|
|
||||||
|
|
||||||
// signed 25 bits required by the standard.
|
|
||||||
using days = std::chrono::duration<int_least32_t, std::ratio<86400>>;
|
|
||||||
|
|
||||||
bool load(handle src, bool) {
|
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
// Lazy initialise the PyDateTime import
|
|
||||||
if (!PyDateTimeAPI) {
|
|
||||||
PyDateTime_IMPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!src) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// If invoked with datetime.delta object
|
|
||||||
if (PyDelta_Check(src.ptr())) {
|
|
||||||
value = type(duration_cast<duration<rep, period>>(
|
|
||||||
days(PyDateTime_DELTA_GET_DAYS(src.ptr()))
|
|
||||||
+ seconds(PyDateTime_DELTA_GET_SECONDS(src.ptr()))
|
|
||||||
+ microseconds(PyDateTime_DELTA_GET_MICROSECONDS(src.ptr()))));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// If invoked with a float we assume it is seconds and convert
|
|
||||||
if (PyFloat_Check(src.ptr())) {
|
|
||||||
value = type(duration_cast<duration<rep, period>>(
|
|
||||||
duration<double>(PyFloat_AsDouble(src.ptr()))));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this is a duration just return it back
|
|
||||||
static const std::chrono::duration<rep, period> &
|
|
||||||
get_duration(const std::chrono::duration<rep, period> &src) {
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
static const std::chrono::duration<rep, period> &
|
|
||||||
get_duration(const std::chrono::duration<rep, period> &&)
|
|
||||||
= delete;
|
|
||||||
|
|
||||||
// If this is a time_point get the time_since_epoch
|
|
||||||
template <typename Clock>
|
|
||||||
static std::chrono::duration<rep, period>
|
|
||||||
get_duration(const std::chrono::time_point<Clock, std::chrono::duration<rep, period>> &src) {
|
|
||||||
return src.time_since_epoch();
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle cast(const type &src, return_value_policy /* policy */, handle /* parent */) {
|
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
// Use overloaded function to get our duration from our source
|
|
||||||
// Works out if it is a duration or time_point and get the duration
|
|
||||||
auto d = get_duration(src);
|
|
||||||
|
|
||||||
// Lazy initialise the PyDateTime import
|
|
||||||
if (!PyDateTimeAPI) {
|
|
||||||
PyDateTime_IMPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare these special duration types so the conversions happen with the correct
|
|
||||||
// primitive types (int)
|
|
||||||
using dd_t = duration<int, std::ratio<86400>>;
|
|
||||||
using ss_t = duration<int, std::ratio<1>>;
|
|
||||||
using us_t = duration<int, std::micro>;
|
|
||||||
|
|
||||||
auto dd = duration_cast<dd_t>(d);
|
|
||||||
auto subd = d - dd;
|
|
||||||
auto ss = duration_cast<ss_t>(subd);
|
|
||||||
auto us = duration_cast<us_t>(subd - ss);
|
|
||||||
return PyDelta_FromDSU(dd.count(), ss.count(), us.count());
|
|
||||||
}
|
|
||||||
|
|
||||||
PYBIND11_TYPE_CASTER(type, const_name("datetime.timedelta"));
|
|
||||||
};
|
|
||||||
|
|
||||||
inline std::tm *localtime_thread_safe(const std::time_t *time, std::tm *buf) {
|
|
||||||
#if (defined(__STDC_LIB_EXT1__) && defined(__STDC_WANT_LIB_EXT1__)) || defined(_MSC_VER)
|
|
||||||
if (localtime_s(buf, time))
|
|
||||||
return nullptr;
|
|
||||||
return buf;
|
|
||||||
#else
|
|
||||||
static std::mutex mtx;
|
|
||||||
std::lock_guard<std::mutex> lock(mtx);
|
|
||||||
std::tm *tm_ptr = std::localtime(time);
|
|
||||||
if (tm_ptr != nullptr) {
|
|
||||||
*buf = *tm_ptr;
|
|
||||||
}
|
|
||||||
return tm_ptr;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is for casting times on the system clock into datetime.datetime instances
|
|
||||||
template <typename Duration>
|
|
||||||
class type_caster<std::chrono::time_point<std::chrono::system_clock, Duration>> {
|
|
||||||
public:
|
|
||||||
using type = std::chrono::time_point<std::chrono::system_clock, Duration>;
|
|
||||||
bool load(handle src, bool) {
|
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
// Lazy initialise the PyDateTime import
|
|
||||||
if (!PyDateTimeAPI) {
|
|
||||||
PyDateTime_IMPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!src) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tm cal;
|
|
||||||
microseconds msecs;
|
|
||||||
|
|
||||||
if (PyDateTime_Check(src.ptr())) {
|
|
||||||
cal.tm_sec = PyDateTime_DATE_GET_SECOND(src.ptr());
|
|
||||||
cal.tm_min = PyDateTime_DATE_GET_MINUTE(src.ptr());
|
|
||||||
cal.tm_hour = PyDateTime_DATE_GET_HOUR(src.ptr());
|
|
||||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
|
||||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
|
||||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
|
||||||
cal.tm_isdst = -1;
|
|
||||||
msecs = microseconds(PyDateTime_DATE_GET_MICROSECOND(src.ptr()));
|
|
||||||
} else if (PyDate_Check(src.ptr())) {
|
|
||||||
cal.tm_sec = 0;
|
|
||||||
cal.tm_min = 0;
|
|
||||||
cal.tm_hour = 0;
|
|
||||||
cal.tm_mday = PyDateTime_GET_DAY(src.ptr());
|
|
||||||
cal.tm_mon = PyDateTime_GET_MONTH(src.ptr()) - 1;
|
|
||||||
cal.tm_year = PyDateTime_GET_YEAR(src.ptr()) - 1900;
|
|
||||||
cal.tm_isdst = -1;
|
|
||||||
msecs = microseconds(0);
|
|
||||||
} else if (PyTime_Check(src.ptr())) {
|
|
||||||
cal.tm_sec = PyDateTime_TIME_GET_SECOND(src.ptr());
|
|
||||||
cal.tm_min = PyDateTime_TIME_GET_MINUTE(src.ptr());
|
|
||||||
cal.tm_hour = PyDateTime_TIME_GET_HOUR(src.ptr());
|
|
||||||
cal.tm_mday = 1; // This date (day, month, year) = (1, 0, 70)
|
|
||||||
cal.tm_mon = 0; // represents 1-Jan-1970, which is the first
|
|
||||||
cal.tm_year = 70; // earliest available date for Python's datetime
|
|
||||||
cal.tm_isdst = -1;
|
|
||||||
msecs = microseconds(PyDateTime_TIME_GET_MICROSECOND(src.ptr()));
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle cast(const std::chrono::time_point<std::chrono::system_clock, Duration> &src,
|
|
||||||
return_value_policy /* policy */,
|
|
||||||
handle /* parent */) {
|
|
||||||
using namespace std::chrono;
|
|
||||||
|
|
||||||
// Lazy initialise the PyDateTime import
|
|
||||||
if (!PyDateTimeAPI) {
|
|
||||||
PyDateTime_IMPORT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get out microseconds, and make sure they are positive, to avoid bug in eastern
|
|
||||||
// hemisphere time zones (cfr. https://github.com/pybind/pybind11/issues/2417)
|
|
||||||
using us_t = duration<int, std::micro>;
|
|
||||||
auto us = duration_cast<us_t>(src.time_since_epoch() % seconds(1));
|
|
||||||
if (us.count() < 0) {
|
|
||||||
us += duration_cast<us_t>(seconds(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract microseconds BEFORE `system_clock::to_time_t`, because:
|
|
||||||
// > If std::time_t has lower precision, it is implementation-defined whether the value is
|
|
||||||
// rounded or truncated. (https://en.cppreference.com/w/cpp/chrono/system_clock/to_time_t)
|
|
||||||
std::time_t tt
|
|
||||||
= system_clock::to_time_t(time_point_cast<system_clock::duration>(src - us));
|
|
||||||
|
|
||||||
std::tm localtime;
|
|
||||||
std::tm *localtime_ptr = localtime_thread_safe(&tt, &localtime);
|
|
||||||
if (!localtime_ptr) {
|
|
||||||
throw cast_error("Unable to represent system_clock in local time");
|
|
||||||
}
|
|
||||||
return PyDateTime_FromDateAndTime(localtime.tm_year + 1900,
|
|
||||||
localtime.tm_mon + 1,
|
|
||||||
localtime.tm_mday,
|
|
||||||
localtime.tm_hour,
|
|
||||||
localtime.tm_min,
|
|
||||||
localtime.tm_sec,
|
|
||||||
us.count());
|
|
||||||
}
|
|
||||||
PYBIND11_TYPE_CASTER(type, const_name("datetime.datetime"));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Other clocks that are not the system clock are not measured as datetime.datetime objects
|
|
||||||
// since they are not measured on calendar time. So instead we just make them timedeltas
|
|
||||||
// Or if they have passed us a time as a float we convert that
|
|
||||||
template <typename Clock, typename Duration>
|
|
||||||
class type_caster<std::chrono::time_point<Clock, Duration>>
|
|
||||||
: public duration_caster<std::chrono::time_point<Clock, Duration>> {};
|
|
||||||
|
|
||||||
template <typename Rep, typename Period>
|
|
||||||
class type_caster<std::chrono::duration<Rep, Period>>
|
|
||||||
: public duration_caster<std::chrono::duration<Rep, Period>> {};
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#include "detail/common.h"
|
|
||||||
#warning "Including 'common.h' is deprecated. It will be removed in v3.0. Use 'pybind11.h'."
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
pybind11/complex.h: Complex number support
|
|
||||||
|
|
||||||
Copyright (c) 2016 Wenzel Jakob <wenzel.jakob@epfl.ch>
|
|
||||||
|
|
||||||
All rights reserved. Use of this source code is governed by a
|
|
||||||
BSD-style license that can be found in the LICENSE file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "pybind11.h"
|
|
||||||
|
|
||||||
#include <complex>
|
|
||||||
|
|
||||||
/// glibc defines I as a macro which breaks things, e.g., boost template names
|
|
||||||
#ifdef I
|
|
||||||
# undef I
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(PYBIND11_NAMESPACE)
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct format_descriptor<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
|
||||||
static constexpr const char c = format_descriptor<T>::c;
|
|
||||||
static constexpr const char value[3] = {'Z', c, '\0'};
|
|
||||||
static std::string format() { return std::string(value); }
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifndef PYBIND11_CPP17
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
constexpr const char
|
|
||||||
format_descriptor<std::complex<T>,
|
|
||||||
detail::enable_if_t<std::is_floating_point<T>::value>>::value[3];
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PYBIND11_NAMESPACE_BEGIN(detail)
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_fmt_numeric<std::complex<T>, detail::enable_if_t<std::is_floating_point<T>::value>> {
|
|
||||||
static constexpr bool value = true;
|
|
||||||
static constexpr int index = is_fmt_numeric<T>::index + 3;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
class type_caster<std::complex<T>> {
|
|
||||||
public:
|
|
||||||
bool load(handle src, bool convert) {
|
|
||||||
if (!src) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!convert && !PyComplex_Check(src.ptr())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Py_complex result = PyComplex_AsCComplex(src.ptr());
|
|
||||||
if (result.real == -1.0 && PyErr_Occurred()) {
|
|
||||||
PyErr_Clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value = std::complex<T>((T) result.real, (T) result.imag);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static handle
|
|
||||||
cast(const std::complex<T> &src, return_value_policy /* policy */, handle /* parent */) {
|
|
||||||
return PyComplex_FromDoubles((double) src.real(), (double) src.imag());
|
|
||||||
}
|
|
||||||
|
|
||||||
PYBIND11_TYPE_CASTER(std::complex<T>, const_name("complex"));
|
|
||||||
};
|
|
||||||
PYBIND11_NAMESPACE_END(detail)
|
|
||||||
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
NOTE
|
|
||||||
----
|
|
||||||
|
|
||||||
The C++ code here
|
|
||||||
|
|
||||||
** only depends on <Python.h> **
|
|
||||||
|
|
||||||
and nothing else.
|
|
||||||
|
|
||||||
DO NOT ADD CODE WITH OTHER EXTERNAL DEPENDENCIES TO THIS DIRECTORY.
|
|
||||||
|
|
||||||
Read on:
|
|
||||||
|
|
||||||
pybind11_conduit_v1.h — Type-safe interoperability between different
|
|
||||||
independent Python/C++ bindings systems.
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
// Copyright (c) 2024 The pybind Community.
|
|
||||||
|
|
||||||
/* The pybind11_conduit_v1 feature enables type-safe interoperability between
|
|
||||||
|
|
||||||
* different independent Python/C++ bindings systems,
|
|
||||||
|
|
||||||
* including pybind11 versions with different PYBIND11_INTERNALS_VERSION's.
|
|
||||||
|
|
||||||
* NOTE: The conduit feature
|
|
||||||
only covers from-Python-to-C++ conversions, it
|
|
||||||
does not cover from-C++-to-Python conversions.
|
|
||||||
(For the latter, a different feature would have to be added.)
|
|
||||||
|
|
||||||
The naming of the feature is a bit misleading:
|
|
||||||
|
|
||||||
* The feature is in no way tied to pybind11 internals.
|
|
||||||
|
|
||||||
* It just happens to originate from pybind11 and currently still lives there.
|
|
||||||
|
|
||||||
* The only external dependency is <Python.h>.
|
|
||||||
|
|
||||||
The implementation is a VERY light-weight dependency. It is designed to be
|
|
||||||
compatible with any ISO C++11 (or higher) compiler, and does NOT require
|
|
||||||
C++ Exception Handling to be enabled.
|
|
||||||
|
|
||||||
Please see https://github.com/pybind/pybind11/pull/5296 for more background.
|
|
||||||
|
|
||||||
The implementation involves a
|
|
||||||
|
|
||||||
def _pybind11_conduit_v1_(
|
|
||||||
self,
|
|
||||||
pybind11_platform_abi_id: bytes,
|
|
||||||
cpp_type_info_capsule: capsule,
|
|
||||||
pointer_kind: bytes) -> capsule
|
|
||||||
|
|
||||||
method that is meant to be added to Python objects wrapping C++ objects
|
|
||||||
(e.g. pybind11::class_-wrapped types).
|
|
||||||
|
|
||||||
The design of the _pybind11_conduit_v1_ feature provides two layers of
|
|
||||||
protection against C++ ABI mismatches:
|
|
||||||
|
|
||||||
* The first and most important layer is that the pybind11_platform_abi_id's
|
|
||||||
must match between extensions. — This will never be perfect, but is the same
|
|
||||||
pragmatic approach used in pybind11 since 2017
|
|
||||||
(https://github.com/pybind/pybind11/commit/96997a4b9d4ec3d389a570604394af5d5eee2557,
|
|
||||||
PYBIND11_INTERNALS_ID).
|
|
||||||
|
|
||||||
* The second layer is that the typeid(std::type_info).name()'s must match
|
|
||||||
between extensions.
|
|
||||||
|
|
||||||
The implementation below (which is shorter than this comment!), serves as a
|
|
||||||
battle-tested specification. The main API is this one function:
|
|
||||||
|
|
||||||
auto *cpp_pointer = pybind11_conduit_v1::get_type_pointer_ephemeral<YourType>(py_obj);
|
|
||||||
|
|
||||||
It is meant to be a minimalistic reference implementation, intentionally
|
|
||||||
without comprehensive error reporting. It is expected that major bindings
|
|
||||||
systems will roll their own, compatible implementations, potentially with
|
|
||||||
system-specific error reporting. The essential specifications all bindings
|
|
||||||
systems need to agree on are merely:
|
|
||||||
|
|
||||||
* PYBIND11_PLATFORM_ABI_ID (const char* literal).
|
|
||||||
|
|
||||||
* The cpp_type_info capsule (see below: a void *ptr and a const char *name).
|
|
||||||
|
|
||||||
* The cpp_conduit capsule (see below: a void *ptr and a const char *name).
|
|
||||||
|
|
||||||
* "raw_pointer_ephemeral" means: the lifetime of the pointer is the lifetime
|
|
||||||
of the py_obj.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// THIS MUST STAY AT THE TOP!
|
|
||||||
#include "pybind11_platform_abi_id.h"
|
|
||||||
|
|
||||||
#include <Python.h>
|
|
||||||
#include <typeinfo>
|
|
||||||
|
|
||||||
namespace pybind11_conduit_v1 {
|
|
||||||
|
|
||||||
inline void *get_raw_pointer_ephemeral(PyObject *py_obj, const std::type_info *cpp_type_info) {
|
|
||||||
PyObject *cpp_type_info_capsule
|
|
||||||
= PyCapsule_New(const_cast<void *>(static_cast<const void *>(cpp_type_info)),
|
|
||||||
typeid(std::type_info).name(),
|
|
||||||
nullptr);
|
|
||||||
if (cpp_type_info_capsule == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
PyObject *cpp_conduit = PyObject_CallMethod(py_obj,
|
|
||||||
"_pybind11_conduit_v1_",
|
|
||||||
"yOy",
|
|
||||||
PYBIND11_PLATFORM_ABI_ID,
|
|
||||||
cpp_type_info_capsule,
|
|
||||||
"raw_pointer_ephemeral");
|
|
||||||
Py_DECREF(cpp_type_info_capsule);
|
|
||||||
if (cpp_conduit == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
void *raw_ptr = PyCapsule_GetPointer(cpp_conduit, cpp_type_info->name());
|
|
||||||
Py_DECREF(cpp_conduit);
|
|
||||||
if (PyErr_Occurred()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return raw_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T *get_type_pointer_ephemeral(PyObject *py_obj) {
|
|
||||||
void *raw_ptr = get_raw_pointer_ephemeral(py_obj, &typeid(T));
|
|
||||||
if (raw_ptr == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return static_cast<T *>(raw_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace pybind11_conduit_v1
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user