Compare commits

..

121 Commits

Author SHA1 Message Date
Chris Hennes 53767601ea Measure: Translate measurement types 2025-11-25 14:53:08 -03:00
Frank Martinez 08dabacfed Build: Remove deprecated pylint option
(cherry picked from commit 863d77562d)
2025-11-25 13:56:58 -03:00
Frank Martinez 9b934a45db Build: Update pylint minimum python version to 3.10
(cherry picked from commit 2ef70f6094)
2025-11-25 13:56:58 -03:00
Louis Gombert a0a8d7760b CMake: fix parsing error in Sandbox/Gui
Introduced by commit 755229d

(cherry picked from commit 0fca304912)
2025-11-25 14:49:51 +01:00
paul 4962bbbffe [ArchWall] Fix Bug: Center-Align with Centered Multi-Material (#25572)
* [ArchWall] Fix Bug: Center-Align with Centered Multi-Material

Fix #25485

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit bcc30869b7)
2025-11-25 08:42:38 +01:00
Bas Ruigrok 7c6d70ab02 Gui: Simplify QObject::connect in openPopupMenu
Co-authored-by: xtemp09 <9303235+xtemp09@users.noreply.github.com>
(cherry picked from commit 6e5a318ceb)
2025-11-25 08:42:30 +01:00
Bas Ruigrok 30e94d0b3f Gui: Fix Gesture style context menu segfault
(cherry picked from commit dec0ea238a)
2025-11-25 08:42:30 +01:00
PaddleStroke 70c69bbebc Assembly: Bom: replace QtWidget by QtGui for Qt6
(cherry picked from commit 59f672eb94)
2025-11-25 08:42:18 +01:00
PaddleStroke 1cd0927a07 Assembly: Joint task: fix spinbox not being populated
(cherry picked from commit 629eff4a26)
2025-11-25 06:04:24 +01:00
Billy Huddleston e5b5fb38a4 CAM: Make CAM tests and serialization robust to locale-dependent decimal separators
Updated CAM unit tests and LinuxCNC serializer to handle decimal separators consistently, ensuring tests pass regardless of system locale. Assertions now compare FreeCAD.Units.Quantity objects directly or normalize decimal separators in strings. LinuxCNC serializer output is forced to use periods for decimals.

src/Mod/CAM/CAMTests/TestPathToolBitListWidget.py:
- Normalize decimal separators in tool description assertions for locale robustness.

src/Mod/CAM/CAMTests/TestPathToolBitPropertyEditorWidget.py:
- Use FreeCAD.Units.Quantity for direct quantity comparisons in property editor tests.

src/Mod/CAM/CAMTests/TestPathToolBitSerializer.py:
- Compare deserialized and serialized quantities using FreeCAD.Units.Quantity for consistency.

src/Mod/CAM/CAMTests/TestPathToolShapeClasses.py:
- Compare parameter values and units directly instead of relying on string formatting.

src/Mod/CAM/Path/Tool/library/serializers/linuxcnc.py:
- Force period as decimal separator in LinuxCNC serializer output to avoid locale issues.

(cherry picked from commit 9cab1f8f52)
2025-11-25 06:03:46 +01:00
FEA-eng e55305fb25 FEM: add new cyclic symm centrif example
(cherry picked from commit d2f082c7bc)
2025-11-25 06:03:29 +01:00
Furgo be96c07587 Assembly: Prevent zero-length line creation in ExplodedView
Safeguards the generation of explosion trail lines to avoid creating
invalid geometry.

Previously, if a part displacement was zero (or effectively zero),
`Part.LineSegment` would attempt to create a line with identical start
and end points, causing OpenCascade to throw a `Part.OCCError`. This
resulted in failures during document restoration and recomputes.

This commit introduces a `_createSafeLine` helper method that checks the
distance against a `1e-7` (near-zero) tolerance before attempting to
generate the shape.

(cherry picked from commit 2763bb962f)
2025-11-24 23:08:54 -03:00
Furgo b5dee9832f App: Handle uncleared Python exceptions during object restoration
When a Python-based object throws an exception during the restoration
process (e.g. inside `onDocumentRestored`), the error is caught by a
generic `catch(...)` block in `Document::afterRestore`.

Previously, this block swallowed the C++ exception wrapper but failed to
clear the underlying Python error state. Leaving the interpreter in this
"dirty" state caused a segmentation fault or `SystemError` later in the
loading process when C++ attempted to interact with the Python API
(specifically in `App::Application::setActiveDocument`).

This commit adds a check for `PyErr_Occurred()` inside the catch block.
If an error is detected, the traceback is printed to the console for
debugging, and `PyErr_Clear()` is called to reset the interpreter state,
allowing the application to continue loading the document without
crashing.

(cherry picked from commit b571889ab5)
2025-11-24 23:08:54 -03:00
PaddleStroke 69f6f9e4b0 Assembly: Joint task: Fix sel gate not being cleared if doc closed
(cherry picked from commit 8b1dce3437)
2025-11-24 23:06:43 -03:00
Chris Hennes 9c24507531 Gui: Switch to defusedxml for document recovery
(cherry picked from commit 4536a54f8f)
2025-11-24 22:45:07 -03:00
Adrian Insaurralde Avalos ada3b7c97e CI: improve release workflow
adapt weekly build workflow to do normal releases too, rename accordingly
skip macos singning setup if certificate not available (useful to run on forks)
add missing dmgbuild dependency for badge icons on macos
build windows installer in workflow, add needed dependencies to pixi.toml
reorganize packaging scripts that can be useful outside rattler-build too
do some cleanup
add .gitignore to rattler-build
Properly configure appimage updating depending on release type and upload zsync file

(cherry picked from commit 46f3cf2f19)
2025-11-24 21:32:52 +01:00
Adrián Insaurralde Avalos bc5b7d66c1 WindowsInstaller: multiple improvements and fixes
move windowsinstaller to /package
update artwork
adapt to conda builds
make msvc redist directory optional
automate version information definition
use relative directories for file locations definitions
improve/update readme
partially update Delete.bat for qt6 libpack
add .gitignore
update signing.bat and add hashing command

WindowsInstaller: use --safe-mode in freecadcmd commands [skip ci]

WindowsInstaller: make windows 8 the minimum version [skip ci]

WindowsInstaller: allow configuring some values via command line

windows installer update for qt6 build

(cherry picked from commit d466ba037b)
2025-11-24 21:32:52 +01:00
Adrian Insaurralde Avalos 9931e01be7 CI: specify environment for fedora-daily workflow
otherwise it doesn't have access to the secrets it needs

(cherry picked from commit a847a794c4)
2025-11-24 21:32:52 +01:00
pre-commit-ci[bot] 7176a5c3bb [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 59715114f5)
2025-11-24 19:19:09 +01:00
chris ac39784aac core: fix various issues exporting step files, ie. #25540 #24962 #25567
(cherry picked from commit e226e9b06e)
2025-11-24 19:19:09 +01:00
chris da432a77af export: step file preserve last feature when exporting step file, fix issue #25567
(cherry picked from commit 9c48c9a3e3)
2025-11-24 19:19:09 +01:00
chris e6e0d03034 core: preserve body name in step file when exporting part that contains body with feature, fixes #24962
(cherry picked from commit 246218b28b)
2025-11-24 19:19:09 +01:00
PaddleStroke c5387aba66 Sketcher: External Face: decide between defining and construction (#25390)
(cherry picked from commit 74aafcee75)
2025-11-24 19:18:59 +01:00
Syres916 1d739f9773 [Gui] Reduce separator from 2px to 1px
(cherry picked from commit fc6a39658a)
2025-11-24 19:18:46 +01:00
Syres916 7c0c421401 [Gui] increase contrast of menu separators and down arrows in comboboxes
(cherry picked from commit cda9a107aa)
2025-11-24 19:18:46 +01:00
Kacper Donat 45c5b33db5 PartDesign: Use true tool positions for boolean preview
This fixes positioning of some previews for booleans that were misplaced
after #24750 was merged. It restores previous code that was correct for
most cases. The reason for some previews being misaligned is described
in the #25578 - the preview actually shows how the result should be but
due to some shortcuts taken in code the result is incorrect.

(cherry picked from commit 88543612e4)
2025-11-24 19:18:40 +01:00
tarman3 501f29a247 CAM: Engrave - Fix #22380 - duplicates 2025-11-24 18:37:13 +01:00
chris 57a1ac5812 part design: reimp feature to move datum objects using modal / dialogue box from right click menu in model tab
(cherry picked from commit 0fc96a1a6f)
2025-11-24 18:37:07 +01:00
marioalexis 680ccc0ecf Fem: Improve VTK import/export preference page
(cherry picked from commit 2457715d84)
2025-11-24 18:26:53 +01:00
pre-commit-ci[bot] 7a7cba5587 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit ada21269aa)
2025-11-24 18:26:53 +01:00
Chris Hennes c90f7d67df FEM: Add missing translation in InOutVTK
(cherry picked from commit 128dec534a)
2025-11-24 18:26:53 +01:00
marioalexis 402032c230 Fem: Improve Gmsh preference page implementation
(cherry picked from commit 7d218b3f9b)
2025-11-24 18:12:56 +01:00
Chris Hennes 4ba4d1b848 FEM: Translate log verbosity settings
(cherry picked from commit 16eb18abb2)
2025-11-24 18:12:56 +01:00
sliptonic 024e904c9a Merge pull request #25554 from tarman3/1-1_leadinout_overtravel6
CAM: 1.1 - LeadInOut - Fix regression after #24829
2025-11-24 11:00:24 -06:00
Kacper Donat 74879b6738 Attacher: Reduce unnecessary rotation around normal for midpoint
(cherry picked from commit 3e356491e2)
2025-11-24 17:48:21 +01:00
PaddleStroke e76fdd82dc Sketcher: Prevent bad coincidences (#25304)
(cherry picked from commit e38154474a)
2025-11-24 17:48:14 +01:00
pjcreath a7486c6fa9 Sketcher: prevent undo/redo crash when selected geometry goes away.
Fixes #25497

(cherry picked from commit efb10e1b28)
2025-11-24 17:32:32 +01:00
pjcreath 4a331340fe Sketcher: prevent crash on undo/redo while dragging.
Fixes #25500

(cherry picked from commit 16aff10544)
2025-11-24 16:00:22 +01:00
xtemp09 e3d0fe6bb6 [GUI] Handle ampersand in windowTitle
(cherry picked from commit eb0c980c1d)
2025-11-24 15:19:44 +01:00
Syres916 8c36c5390a [Gui] Fix string coding on selection changed
(cherry picked from commit a10fbf2e8c)
2025-11-24 08:00:33 +01:00
Syres916 2ed6528d61 [Part] Fix string encoding for document name and object name
(cherry picked from commit 76071ffbeb)
2025-11-24 08:00:33 +01:00
Syres916 1c54777405 [Gui] Fix string encoding for document name
(cherry picked from commit 83c01fae68)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] 4cd7fdc2df [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 6d8c5bd2f9)
2025-11-24 08:00:33 +01:00
Syres916 c637b532f1 [Gui] Fix string encoding for preselect, select and deselect
(cherry picked from commit eb08fe2a78)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] d5da0227ee [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 23df7e4cad)
2025-11-24 08:00:33 +01:00
Syres916 3446b933cb [Gui] Fix string encoding for document, object and subojects
(cherry picked from commit d19ab48231)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] 6f999f8885 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 78bb40c419)
2025-11-24 08:00:33 +01:00
Syres916 95c547bd5a [PartDesign] Fix string encoding for document name and face reference
(cherry picked from commit 1e30362dd4)
2025-11-24 08:00:33 +01:00
Syres916 7aed77aaec [Gui] Fix string encoding for document name
(cherry picked from commit 61db439744)
2025-11-24 08:00:33 +01:00
pre-commit-ci[bot] aa02543896 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci

(cherry picked from commit 4c8d140373)
2025-11-24 08:00:33 +01:00
Syres916 a6da44aa03 [Gui] Fix string encoding document name and object name
(cherry picked from commit fcd487e0ad)
2025-11-24 08:00:33 +01:00
Syres916 c8f629b8b4 [Gui] Fix incorrect document name when updating PropertyLink
(cherry picked from commit 093a070439)
2025-11-24 08:00:33 +01:00
chris b71344c07b sketcher: fix issue #13852 resolve issue with state not updating constraint text
(cherry picked from commit d806b4e5f3)
2025-11-24 06:11:50 +01:00
PaddleStroke 8e73bddec1 Sketcher: Rotate: Fix line length ignored (#25434)
(cherry picked from commit 1d30d079b0)
2025-11-24 06:11:21 +01:00
Roy-043 aa010759d3 Draft: fix number_length for imperial dimensions (#25369)
* The number_length function did not take imperial dimensions into account. For `9" + 7/8"` it would return 1.
* In ArchRoof reference number_length function from DraftGui.py to avoid duplicate code.

(cherry picked from commit 98240a6de5)
2025-11-24 06:11:04 +01:00
FEA-eng 383a51075c FEM: Add Thick Pipe Internal Pressure 2D example (#25191)
Co-authored-by: Kacper Donat <kadet1090@gmail.com>
(cherry picked from commit 44bf1c35d6)
2025-11-24 06:10:44 +01:00
PaddleStroke aeb6fca064 Assembly: BOM: fix subassembly
(cherry picked from commit 40e4eb8770)
2025-11-24 06:10:22 +01:00
Chris 32bfee0ada Sketcher: allow grid snapping while hovering mouse over axis (#25226)
(cherry picked from commit 72d021108f)
2025-11-24 06:10:06 +01:00
Billy Huddleston cb4d6b9cb7 CAM: Ensure Chipload is restored and displayed with user-preferred units
Fixes an issue where Chipload and similar mixin-defined properties were added to
the schema but not reliably restored, because parameter restoration did not always
apply them to the toolbit object. This change ensures Chipload is restored from saved
data and displayed using the user's preferred unit schema, even when no document
is open. This update also ensures the toolbit editor respects the user’s unit schema
preference during editing, even outside an open document.

src/Mod/CAM/Path/Tool/toolbit/models/base.py:
- Restore Chipload (and other parameters) to toolbit.obj if the property exists.

src/Mod/CAM/Path/Tool/toolbit/ui/editor.py:
- Set units schema to user preference when opening the toolbit editor, so Chipload and other values display correctly.

(cherry picked from commit 4432575243)
2025-11-23 20:40:39 +01:00
sliptonic d2a99e83f7 Merge pull request #25573 from FreeCAD/backport-22738-to-releases/FreeCAD-1-1
[Backport releases/FreeCAD-1-1] CAM: Pocket BSplineSurface
2025-11-23 12:41:17 -06:00
sliptonic 78df9dd937 Merge pull request #25575 from FreeCAD/backport-25106-to-releases/FreeCAD-1-1
[Backport releases/FreeCAD-1-1] [CAM] Offer automatic migration of tools in (old) custom working directory to the new tool system
2025-11-23 12:40:58 -06:00
David Kaufman 0b92c77685 Hopefully placate CodeQL
(cherry picked from commit 767ee484ba)
2025-11-23 18:34:06 +00:00
David Kaufman b73a2cca0d migrate silently, no prompts
(cherry picked from commit f22d0e2292)
2025-11-23 18:34:06 +00:00
David Kaufman 205d024b74 remove unused import
(cherry picked from commit e2b4ea58b8)
2025-11-23 18:34:06 +00:00
David Kaufman fc317b16b0 automatically offer migration for tool libraries in custom working dir
(cherry picked from commit 21319f95d2)
2025-11-23 18:34:06 +00:00
David Kaufman 3b80c25339 [CAM] bugfix tools not imported properly during library import
(cherry picked from commit bb9ae3540e)
2025-11-23 18:34:06 +00:00
pre-commit-ci[bot] ccd3e778ff [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-23 18:06:12 +00:00
tarman3 7bf96e2ef0 CAM: LeadInOut - Fix regression after #24829 2025-11-23 20:02:15 +02:00
tarman3 387c3be2bf CAM: Pocket BSplineSurface
(cherry picked from commit 889dc7059c)
2025-11-23 18:02:10 +00:00
sliptonic 2c775840da Merge pull request #25403 from Connor9220/backport-24829-to-releases/FreeCAD-1-1
CAM: LeadInOut - Fix overtravel
2025-11-23 11:46:45 -06:00
sliptonic 14d93d24fb Merge pull request #25438 from Connor9220/FixBullnoseToolBit
CAM: Introduce unified ToolBit parameter migration logic for Bullnose tools
2025-11-23 11:39:34 -06:00
Andrew Burks c7b3353414 #25474 Added read-only warnings when saving documents (#25532)
* #25474 Added read-only warnings when saving documents

Block saving a file and notify user if windows is unable to save to the file for any reason, or the read-only attribute is checked. Also check std::filesystem::perms for write permission and other checks in FileInfo::isWritable(), although it doesn't seem to matter on windows.

Co-authored-by: Chris Hennes <chennes@pioneerlibrarysystem.org>
(cherry picked from commit bad2989b7b)
2025-11-23 07:24:49 +01:00
Adrian Insaurralde Avalos a469c45a2d pixi: enable building reverse engineering
(cherry picked from commit 3ab8dbe194)
2025-11-21 16:54:37 -06:00
Roy-043 4e6bb3f524 BIM: fix handling of BIM_Sketch view properties (#25339)
* BIM: fix handling of BIM_Sketch view properties

The handling of the sketch view properties was not correct:
* AutoColor was not set to `False`.
* PointSize was missing.
* PointColor was not set to the DefaultShapeVertexColor preference.
* Grid values were never applied. Sketches do not have a GridSnap property and gridSize is not the correct Draft preference.
* Sketch grid snap is a global setting that affects all sketches, the code should not change that setting IMO.

* Remove trailing white space

(cherry picked from commit 63c9b176e2)
2025-11-21 11:40:34 +01:00
Logstor ba5652e046 Removed sys in 'from os import sys, path'
(cherry picked from commit e802aa1821)
2025-11-21 08:47:32 +01:00
Jacob Oursland 40cd4fe772 Enable Reverse Engineering WB.
The Reverse Engineering WB was temporarily disabled as it didn't support Qt6.As it now supports Qt6,
it is time to re-enable the WB.

(cherry picked from commit f48db1f855)
2025-11-21 08:08:55 +01:00
Adrian Insaurralde Avalos 5f68507f6d set 1.1.0rc1 version in packaging files 2025-11-20 23:14:04 -03:00
PaddleStroke ae864aedb4 Assembly: Make the 'ground first part' dialog closable
(cherry picked from commit 2266a42009)
2025-11-20 07:58:52 +01:00
Chris Hennes d4f2c904f8 Gui: Translate 'Don't show me again'
(cherry picked from commit deab0abf8e)
2025-11-20 07:57:37 +01:00
Kacper Donat 2ff62e9727 Gui: Fix stylesheet loading order
For some platforms due to event loop and timer triggering being not
deterministic stylesheets could be loaded before style parameters were
fully loaded. This caused warnings about style parameters to appear
potentially confusing users.

This commit changes the stylesheet loading to happen much earlier in the
process so the stylesheet is applied before theme preselection has
chance to happen.

(cherry picked from commit 8995f43a15)
2025-11-19 20:18:03 +01:00
Kacper Donat 302fbc6765 Gui: Save defaults layout after first launch
(cherry picked from commit d1a8f99509)
2025-11-19 18:41:30 +01:00
PaddleStroke 456bc178c6 Assembly: Fix joint on Body-LCS not working
(cherry picked from commit 1cf6f5f3a6)
2025-11-19 14:09:37 +01:00
PaddleStroke 66ed85bb19 Assembly/TechDraw: Fix exploded view views (#25426)
(cherry picked from commit be7d41dea7)
2025-11-18 15:59:27 -06:00
pre-commit-ci[bot] cd2e26bbea [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-18 20:20:45 +00:00
Billy Huddleston 6058a47f58 Cleanup for 1.1 2025-11-18 15:17:45 -05:00
PaddleStroke 092ae3f8ea Assembly: Fix crash on moving unconnected parts
(cherry picked from commit 2a8d648ad3)
2025-11-18 20:28:06 +01:00
Billy Huddleston 3971ce2bf7 CAM: Introduce unified ToolBit parameter migration logic for Bullnose tools
- Added new migration system to handle legacy parameter conversion for ToolBit assets and objects.
- Implemented ParameterAccessor abstraction for consistent access to dicts and FreeCAD objects.
- Centralized migration logic for CornerRadius from TorusRadius or FlatRadius/Diameter, restricted to Bullnose shape-type.
- Updated asset and object initialization to use migration logic and filter Bullnose parameters.

src/Mod/CAM/Path/Tool/camassets.py:
- Integrate new migration logic for ToolBit assets using ParameterAccessor and migrate_parameters.
- Ensure shape-type is set before parameter migration; only write changes if migration occurred.

src/Mod/CAM/Path/Tool/toolbit/migration.py:
- Add ParameterAccessor class and migrate_parameters function for unified migration.
- Handle legacy parameter conversion for Bullnose tools.

src/Mod/CAM/Path/Tool/toolbit/models/base.py:
- Apply migration logic to ToolBit objects on restore.
- Filter Bullnose parameters to remove FlatRadius after migration.

src/Mod/CAM/Path/Tool/shape/models/bullnose.py:
- Add filter_parameters method to remove FlatRadius for Bullnose.

src/Mod/CAM/Path/Op/SurfaceSupport.py:
- Derive FlatRadius from CornerRadius and Diameter if both are present.

src/Mod/CAM/Path/Op/Surface.py:
- Remove obsolete setOclCutter method.
2025-11-18 13:38:39 -05:00
Chris 69498be228 Part: fix splitting of a split that is a container / group (#25293)
(cherry picked from commit 3f42824ffa)
2025-11-18 19:22:57 +01:00
tetektoza 08cceec9cf Gui: Use UTF-8 string instead of UTF-16 for Reset in Expression Editor
Currently `ExpLineEdit::apply()` passes `QString::constData()` to a
printf-style format string with %s. `QString::constData() returns a
const QChar* (UTF-16, 2 bytes per char), but %s expects a const char*
(UTF-8/ASCII with 1 byte per char).

This results in a string being interpreted as in "Container":
Byte 0: 'C'
Byte 1: 0x00 (high byte of UTF-16 'C', which gets interpreted as null
terminator)

So this patch uses proper conversion to null-terminated C-String before
passing it further.

(cherry picked from commit ca1528e2a9)
2025-11-18 17:51:51 +01:00
tetektoza 3c6cdb17e0 Gui: Differentiate completion activation modes in Expression Editor
Currently if user tries to press TAB during Expression Editor, it
inserts both the entry and its first subentry. Also, if user browses the
dropdown with arrows keys, it inserts it's values.

Root cause of that is the regression made in latest changes to chaining
completion logic which is triggered for all completion modes including
TAB, which already has its own refresh mechanism. Also,
ExpressionTextEdit connected both activated (Enter/click) and highlight
signals to the same slot, resulting in arrow key navigation inserting
completions.

So, this adds separate slots for ExpressionTextEdit to differentiate
completion modes. And also updates tab handling to pass
ActivationMode::Highlighted to prevent double chaining.

(cherry picked from commit 3f6a0ee78b)
2025-11-18 17:51:51 +01:00
PaddleStroke 15de64e30b Sketcher: ConstraintList: prevent N transaction on box selection (#25254)
(cherry picked from commit 6f90c5ea61)
2025-11-18 09:18:59 -06:00
captain0xff d936ba474c PartDesign: fix the interactive controls for revolution/groove
(cherry picked from commit 5d387ae014)
2025-11-18 16:11:54 +01:00
Billy Huddleston b44af1f768 CAM: Fix crash when Path panel widgets are deleted
Prevent RuntimeError by safely handling missing or deleted geometry import
widgets in Path operation panels. This ensures the UI update code does not
fail if the underlying C++ objects are destroyed, improving stability when
panels are closed or the UI changes.

src/Mod/CAM/Path/Op/Base.py:
- Wrap geometry import widget access in try/except to avoid crashes
- Add error handling for panel widget deletion scenarios

(cherry picked from commit 900d5ae255)
2025-11-18 14:12:09 +01:00
Chris Hennes f308fb92a3 Gui: Fix context of translation strings
(cherry picked from commit 0aec9c3b21)
2025-11-18 14:11:52 +01:00
Gaël Écorchard 98a1dd2f14 Remove functions that require Kuka files (#24984)
(cherry picked from commit c77e1d840c)
2025-11-18 14:11:42 +01:00
Chris Hennes 0887aa188b Gui: Add translation to 'No style sheet'
(cherry picked from commit 0a73f53990)
2025-11-18 14:11:31 +01:00
PaddleStroke 9a4cedf1a4 Assembly: Sub-assembly: fix support for Part-wb objects (#25279)
* Assembly: Sub-assembly: fix support for Part-wb objects

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit 551ab85076)
2025-11-18 11:05:47 +01:00
tarman3 894e1d9ae2 CAM: Engrave - Fix #9114 - Unable to engrave an arc
(cherry picked from commit a514cfdf4b)
2025-11-18 11:04:48 +01:00
PaddleStroke 37f014998c Sketcher: Remove transaction from TaskSketcherConstraints::doSetVisible (#25255)
* Sketcher: Remove transaction from TaskSketcherConstraints::doSetVisible

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit 33d1d80555)
2025-11-18 11:04:42 +01:00
chris 1d21e56ee1 sketcher wb: fix issue #25076, resolve grid snapping placement
(cherry picked from commit 1ec1f1506e)
2025-11-18 11:04:32 +01:00
PaddleStroke c43b615301 Assembly: Insert link: Handle partially loaded documents (#25275)
(cherry picked from commit 10cc4dea16)
2025-11-18 09:05:59 +01:00
Chris 125d3b65c7 Import: STEP export preserve body names (#25042)
(cherry picked from commit f218c5f25c)
2025-11-18 08:14:09 +01:00
pre-commit-ci[bot] 9946819e9f [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-18 05:38:37 +01:00
Chris Hennes 06d55d50ce Build: Add network-failure fault tolerance to macOS signing (#25265)
(cherry picked from commit e0c5c89bd9)
2025-11-18 05:38:37 +01:00
pre-commit-ci[bot] 1678981185 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2025-11-17 20:23:55 +00:00
tarman3 4e2d2e3206 CAM: LeadInOut - Fix overtravel
(cherry picked from commit 2e3a4d541c)
2025-11-17 15:15:04 -05:00
David Carter 3fc8528692 Materials: Assigning material without appearance
Assigning a material without an appearance reset the appearance to the
default appearance.

There were two main problems. One was the comparison function for
App::Material objects. It would return false when the UUID or MatType
values were different although there are many circumstances where this
could be true and the appearance be the same. It also incorrectly
compared the imagePath.

The second problem was the logic for detecting if an object has already
been assigned an appearance by assigning a material or manually setting
the appearance. If assigned a material, the appearance should update but
not if it has been set manually. This logic has been corrected.

(cherry picked from commit f4e78e3163)
2025-11-17 18:32:15 +01:00
Chris Hennes 6508023e95 CI: Update macOS Intel target version in workflow
GitHub is deprecating macOS 13 runners -- the only remaining Intel runner is now macOS 15, and it is scheduled for retirement in 2027, at which point we will no longer be able to provide Intel weeklies.

(cherry picked from commit 50392855bf)
2025-11-17 18:07:17 +01:00
Chris Hennes ce6d9f361a Core: Convert transparency to alpha (#24891)
* Core: Convert transparency to alpha

Create new `Base::getVersion()` function for extracting a program
version enumeration given a version string.

Convert transparency to alpha value for old project files.

* Base/App: Address review comments

---------

Co-authored-by: wmayer <wmayer@freecad.org>
(cherry picked from commit 4d0c35dd2d)
2025-11-17 17:18:42 +01:00
Florian Foinant-Willig 95fe960485 TechDraw: fix "%.0w" format spec (#25367)
(cherry picked from commit 526f7f2550)
2025-11-17 06:41:03 +01:00
Connor9220 49d4375f70 Fix carriage returns in the Migrator Dialog Title. (#25379)
(cherry picked from commit 11eba3f25c)
2025-11-16 22:53:36 -06:00
Jiří Pinkava b0ffa6728c Tests: Fix value for Imperial pressure tests
The decimal was (wrongly) cahnged in 5379378626

(cherry picked from commit 5fc650b2ff)
2025-11-16 22:48:42 -06:00
PaddleStroke 38fccbacf6 TaskAttacher: Remove 'reset edit' causing trouble to assembly (#25277)
(cherry picked from commit 5da1e3d899)
2025-11-15 17:33:43 +01:00
theo-vt a8ba9b82a5 Sketcher: Proceed to chamfer/fillet on a vertex if there are more than 2 lines but only two non-construction lines
(cherry picked from commit 15d9e14851)
2025-11-15 10:09:45 -06:00
theo-vt 647877384a Sketcher.planegcs: solve reference constraints with lower priority (#25318)
(cherry picked from commit d4bfa82161)
2025-11-15 10:08:53 -06:00
Pieter Hijma a60b4698be Doc: Improve the documentation of transactions (#21494)
(cherry picked from commit 0083364dbe)
2025-11-15 08:03:16 +01:00
Roy-043 1a7fa655f3 Core: workaround for Building US unit system bug (#25288)
* Core: workaround for Building US unit system bug

Fixes #11345

This workaround should hopefully fix the Building US unit system bug at the level of the InputField code. This is the most feasible solution given that we are currently in the v1.1 feature freeze.

I use the word "hopefully" because I have not compiled and tested the code. But replacing `+` with `--` works in Python examples.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Fix typo in comment

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit c422404020)
2025-11-15 06:24:11 +01:00
tarman3 6d023afaac CAM: DressupArray - Fix python import
(cherry picked from commit b0a79a6f2c)
2025-11-14 19:33:48 +01:00
Furgo 4642fecba3 Define DlgScale task panel's tab order (#25300)
* Define DlgScale task panel's tab order

To improve keyboard navigation.

Fixes: https://github.com/FreeCAD/FreeCAD/issues/23772

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
(cherry picked from commit 93868d54b7)
2025-11-14 19:33:16 +01:00
Stephan T. Lavavej 2ae9f89982 Avoid using non-Standard std::locale::empty() for MSVC
(cherry picked from commit 9e6509a3a4)
2025-11-14 19:32:50 +01:00
Chris Hennes a6ec30a8fc Build: Change package version suffix from 'dev' to 'rc1' 2025-11-13 08:53:47 -06:00
5009 changed files with 382376 additions and 695638 deletions
@@ -1,92 +0,0 @@
name: Report a Problem
description: Have you found something that does not work well, is too hard to do or is missing altogether? Please create a Problem Report.
labels: ["Status: Needs triage","Status: Needs confirmation"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this problem report! Please [search](https://github.com/FreeCAD/FreeCAD/issues) if a similar issue already exists and check out [how to report issues](https://github.com/FreeCAD/FreeCAD?tab=readme-ov-file#reporting-issues). By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FreeCAD/FreeCAD/blob/main/CODE_OF_CONDUCT.md).
- type: textarea
id: description
attributes:
label: Problem description
description: Describe the problem and how it impacts user experience, workflow, maintainability or performance. You can attach images or log files by clicking this area to highlight it and then dragging files in. To attach a FCStd file, ZIP it first.
placeholder: Describe your problem briefly.
validations:
required: true
- type: dropdown
id: wb
attributes:
label: Workbench affected?
options:
- Assembly
- BIM
- CAM
- Core (App, Gui,...)
- Draft
- FEM
- Material
- Measurement
- Mesh
- Part
- Part Design
- Sketcher
- Spreadsheet
- TechDraw
- Other (specify in description)
- type: textarea
id: steps_to_reproduce
attributes:
label: Steps to reproduce
description: If the problem appears to be a bug with the current functionality, provide a test case or recipe that reliably reproduces the issue. Ideally [record a macro](https://wiki.freecad.org/Std_DlgMacroRecord) and attach it. Please also add an example file as ZIP.
placeholder: |
Drag an example file as ZIP into this textbox to attach it.
Steps to reproduce the behavior:
1. Open my example file
2. Go to '...'
3. Click on '...'
validations:
required: true
- type: textarea
id: expected_behavior
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen when following the provided steps above.
placeholder: What is the expected behavior?
validations:
required: true
- type: textarea
id: actual_behavior
attributes:
label: Actual behavior
description: A clear and concise description of what actually happens. If applicable, add screenshots to help explain the problem.
placeholder: What is actually happening? You can add screenshorts or videos by dragging them into this textbox.
validations:
required: true
- type: textarea
id: dev_version
attributes:
label: Development version About Info (in Safe Mode)
description: Download the latest weekly [development release](https://github.com/FreeCAD/FreeCAD/releases) and try reproducing the issue in safe mode (Help → Restart in Safe Mode). Use the [About FreeCAD](https://wiki.freecad.org/About) dialog to copy your full version information and paste it here.
placeholder: |
1. Download the latest weekly development version (link above).
2. Make sure to run in Safe Mode (Help -> Restart in Safe Mode) to exclude interference from 3rd party addons.
3. If the issue is still reproducible, open the About FreeCAD dialog and copy the full version info here.
render: shell
validations:
required: true
- type: textarea
id: good_version
attributes:
label: Last known good version (optional)
description: If this is a regression, paste the [About FreeCAD](https://wiki.freecad.org/About) info from the last known working version.
placeholder: If the problem did not exist in a previous version, paste the About FreeCAD info from that version here.
render: shell
@@ -1,103 +0,0 @@
name: Report a Code Quality Issue
description: Report problems related to code structure, maintainability, performance, correctness, or technical debt that do not directly affect end-user behavior.
labels: ["Status: Needs triage", "Status: Needs confirmation", "Type: Code Quality"]
type: "Code Quality"
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to help improve FreeCAD's code quality!
> [!NOTE]
> This form is intended **only for code quality issues**, such as:
> - Architectural or design problems
> - Maintainability or readability issues
> - Performance or scalability concerns
> - Incorrect abstractions, layering violations, or technical debt
>
> If the issue affects user-visible behavior, workflows, or UI, please use the regular **Report Problem** form instead.
Please [search existing issues](https://github.com/FreeCAD/FreeCAD/issues) before submitting.
By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FreeCAD/FreeCAD/blob/main/CODE_OF_CONDUCT.md).
- type: dropdown
id: wb
attributes:
label: Area / Workbench affected
description: Select the primary area affected by the code quality issue.
options:
- Assembly
- BIM
- CAM
- Core (App, Gui, Base, ...)
- Draft
- FEM
- Material
- Measurement
- Mesh
- Part
- Part Design
- Sketcher
- Spreadsheet
- TechDraw
- Build system / CI
- Documentation
- Other (specify below)
- type: textarea
id: description
attributes:
label: Problem description
description: |
Describe the code quality issue clearly.
Focus on *why* the current implementation is problematic (e.g. maintainability, performance, correctness, extensibility).
Reference files, classes, functions, or modules where applicable.
placeholder: |
Example:
- Class X violates dependency rules by including Y
- Function Z mixes responsibilities and is hard to reason about
- Algorithm has unnecessary complexity or poor performance characteristics
validations:
required: true
- type: textarea
id: impact
attributes:
label: Impact
description: |
Explain the practical impact of this issue.
For example: increased maintenance cost, higher bug risk, performance degradation, difficulty extending functionality, or CI/build issues.
placeholder: Describe how this affects the codebase long-term.
validations:
required: true
- type: textarea
id: proposed_solution
attributes:
label: Suggested improvement (optional)
description: |
If you have ideas on how to address the issue, outline them here.
This can include refactoring suggestions, alternative designs, or references to best practices.
placeholder: Optional — leave empty if unsure.
- type: textarea
id: references
attributes:
label: References / evidence
description: |
Add relevant references such as:
- File paths or code snippets
- Related issues or pull requests
- Benchmarks, logs, or static analysis results
placeholder: Links, snippets, or related issues.
- type: textarea
id: dev_version
attributes:
label: Development version (if relevant)
description: |
If the issue is version-specific or recently introduced, paste the output from the
**About FreeCAD** dialog of a development build.
Otherwise, this can be left empty.
placeholder: Paste About FreeCAD information here.
render: shell
+73
View File
@@ -0,0 +1,73 @@
name: Report a Problem
description: Have you found something that does not work well, is too hard to do or is missing altogether? Please create a Problem Report.
labels: ["Status: Needs triage","Status: Needs confirmation"]
body:
- type: checkboxes
id: existing_issue
attributes:
label: Is there an existing issue for this?
description: Please [search](https://github.com/FreeCAD/FreeCAD/issues) to see if a similar issue already exists for the problem you encountered.
options:
- label: I have searched the existing issues
required: true
- type: textarea
id: description
attributes:
label: Problem description
description: Describe the problem and how it impacts user experience, workflow, maintainability or speed of the code. If the problem appears to be a bug with the current functionality, provide as test case or recipe that reproduces the error. Ideally record a macro and attach it.
placeholder: Description of the problem
validations:
required: true
- type: textarea
id: full_version
attributes:
label: Full version info
description: Please use the About FreeCAD dialog to copy your full version information and paste it here. Try reproducing the issue by restarting FreeCAD in safe mode too.
render: shell
validations:
required: true
- type: dropdown
id: wb
attributes:
label: Subproject(s) affected?
options:
- Addon Manager
- Assembly
- BIM
- CAM
- Core
- Draft
- Expressions
- FEM
- File formats
- Material
- Measurement
- Mesh
- OpenSCAD
- Part
- PartDesign
- Project Tools & Websites
- Sketcher
- Spreadsheet
- TechDraw
- Other (specify in description)
- type: textarea
id: anything_else
attributes:
label: Anything else?
description: |
Links? References? Anything that will give us more context about the issue you are encountering!
If there is a discussion about the problem on the forum, provide link(s) here.
You can upload or copy your macro here to speed up the diagnosis and debugging.
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in. To attach a FCStd file, ZIP it first (GitHub won't recognize the extension otherwise).
validations:
required: false
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: By submitting this issue, you agree to follow our [Code of Conduct](https://github.com/FreeCAD/FreeCAD/blob/main/CODE_OF_CONDUCT.md)
options:
- label: I agree to follow this project's Code of Conduct
required: true
+2 -2
View File
@@ -58,7 +58,7 @@ jobs:
logdir: /tmp/log/
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -108,7 +108,7 @@ jobs:
done
- name: Upload logs
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ github.job }}-Logs
path: |
+2 -12
View File
@@ -26,21 +26,14 @@
name: FreeCAD master CI
on:
workflow_dispatch: ~
push:
branches:
- main
- releases/**
pull_request: ~
merge_group: ~
on: [workflow_dispatch, push, pull_request, merge_group]
concurrency:
group: FC-CI-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
Prepare:
uses: ./.github/workflows/sub_prepare.yml
with:
@@ -54,21 +47,18 @@ jobs:
Ubuntu:
needs: [Prepare]
if: "!startsWith(github.head_ref, 'refs/heads/backport-')"
uses: ./.github/workflows/sub_buildUbuntu.yml
with:
artifactBasename: Ubuntu-${{ github.run_id }}
Windows:
needs: [Prepare]
if: "!startsWith(github.head_ref, 'refs/heads/backport-')"
uses: ./.github/workflows/sub_buildWindows.yml
with:
artifactBasename: Windows-${{ github.run_id }}
Lint:
needs: [Prepare]
if: "!startsWith(github.head_ref, 'refs/heads/backport-')"
uses: ./.github/workflows/sub_lint.yml
with:
artifactBasename: Lint-${{ github.run_id }}
@@ -68,12 +68,6 @@ runs:
testCommand: ${{ inputs.builddir }}/tests/Gui_tests_run --gtest_output=json:${{ inputs.reportdir }}gui_gtest_results.json
testLogFile: ${{ inputs.reportdir }}gui_gtest_test_log.txt
testName: Gui
- name: C++ Qt tests
id: qttests
uses: ./.github/workflows/actions/runCPPTests/runQtTests
with:
builddir: ${{ inputs.builddir }}
testLogFile: ${{ inputs.reportdir }}qt_ctest_log.txt
- name: C++ Material tests
id: material
uses: ./.github/workflows/actions/runCPPTests/runSingleTest
@@ -1,55 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileNotice: Part of the FreeCAD project.
name: runQtTests
description: "Run all QtTest-based C++ tests (those labelled 'Qt' in CTest), generate log and report"
inputs:
builddir:
description: "Build directory containing CTestTestfile.cmake"
required: true
testLogFile:
description: "Path for the command-line output of the tests"
required: true
outputs:
reportText:
description: "Report text"
value: ${{ steps.report.outputs.reportText }}
runs:
using: "composite"
steps:
- name: Run Qt tests via CTest
shell: bash -l {0}
env:
BUILD_DIR: ${{ inputs.builddir }}
TEST_LOG_FILE: ${{ inputs.testLogFile }}
run: |
set -o pipefail
ctest --test-dir "$BUILD_DIR" -L Qt --output-on-failure | tee -a "$TEST_LOG_FILE"
- name: Parse test results
if: always()
id: report
shell: bash -l {0}
env:
TEST_LOG_FILE: ${{ inputs.testLogFile }}
run: |
result=$(grep -E "(Test #|tests passed|tests failed|Total Test time)" "$TEST_LOG_FILE" || true)
if grep -qE "[1-9][0-9]* tests failed" "$TEST_LOG_FILE"
then
reportText="<details><summary>:fire: QtTest C++ tests failed</summary>\n"
else
reportText="<details><summary>:heavy_check_mark: QtTest C++ tests succeeded</summary>\n"
fi
reportText+="\n"
reportText+="Results\n"
reportText+="\n"
reportText+='```\n'
reportText+="$result\n"
reportText+='```\n'
reportText+="</details>\n"
reportText+="\n"
echo "reportText<<EOD" >> $GITHUB_OUTPUT
echo -e "$reportText" >> $GITHUB_OUTPUT
echo "EOD" >> $GITHUB_OUTPUT
echo -e "$reportText"
@@ -1,62 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# SPDX-FileNotice: Part of the FreeCAD project.
name: runSingleQtTest
description: "Run a single QtTest-based C++ test executable, generate log and report"
inputs:
testCommand:
description: "Test executable path"
required: true
testLogFile:
description: "Path for the command-line output of the tests"
required: true
testName:
description: "A descriptive name for the test suite"
required: true
outputs:
reportText:
description: "Report text"
value: ${{ steps.report.outputs.reportText }}
runs:
using: "composite"
steps:
- name: Run QtTest tests
shell: bash -l {0}
env:
QT_QPA_PLATFORM: offscreen
TEST_COMMAND: ${{ inputs.testCommand }}
TEST_LOG_FILE: ${{ inputs.testLogFile }}
run: |
set -o pipefail
$TEST_COMMAND | tee -a "$TEST_LOG_FILE"
- name: Parse test results
if: always()
id: report
shell: bash -l {0}
env:
TEST_LOG_FILE: ${{ inputs.testLogFile }}
TEST_NAME: ${{ inputs.testName }}
run: |
# This bonkers sed regex is to try to find the start and end of the QtTest output block, which is not really
# designed to be machine-readable. Why did they have to choose asterisks?!?!
result=$(sed -n '/^\*\*\*\*\*\*\*\*\* Start/,/^\*\*\*\*\*\*\*\*\* Finished/p' "$TEST_LOG_FILE" || true)
if grep -qE "^FAIL!" "$TEST_LOG_FILE"
then
reportText="<details><summary>:fire: QtTest C++ test suite '${TEST_NAME}' failed</summary>\n"
else
reportText="<details><summary>:heavy_check_mark: QtTest C++ test suite '${TEST_NAME}' succeeded</summary>\n"
fi
reportText+="\n"
reportText+="Results\n"
reportText+="\n"
reportText+='```\n'
reportText+="$result\n"
reportText+='```\n'
reportText+="</details>\n"
reportText+="\n"
echo "reportText<<EOD" >> $GITHUB_OUTPUT
echo -e "$reportText" >> $GITHUB_OUTPUT
echo "EOD" >> $GITHUB_OUTPUT
echo -e "$reportText"
@@ -21,23 +21,23 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: '🧹 Tag & close stale unconfirmed bugs'
id: stale_issues
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 90
days-before-issue-stale: 60
days-before-issue-close: 14
days-before-pr-stale: -1
days-before-pr-close: -1
operations-per-run: 200 # max num of ops per run
operations-per-run: 100 # max num of ops per run
stale-issue-label: 'Status: Stale'
close-issue-label: 'Status: Auto-closing'
exempt-issue-labels: 'Status: Confirmed,Priority: High,Priority: Critical,Blocker,Type: Feature,no-auto-close,3rd party: OCC'
exempt-issue-labels: 'Status: Confirmed,Priority: High,Priority: Critical,Blocker,Type: Feature,no-auto-close'
remove-stale-when-updated: true
ascending: true
stale-issue-message: |
@@ -53,7 +53,7 @@ jobs:
- name: '🧹 Close stale requested feedback issues'
id: awaiting_issues
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 20
@@ -80,17 +80,17 @@ jobs:
- name: '🧹 Tag & close inactive issues'
id: inactive_issues
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: 190
days-before-issue-close: 60
days-before-pr-stale: -1
days-before-pr-close: -1
operations-per-run: 100 # max num of ops per run
operations-per-run: 50 # max num of ops per run
stale-issue-label: 'Status: Stale'
close-issue-label: 'Status: Auto-closing'
exempt-issue-labels: 'Priority: High,Priority: Critical,Blocker,Type: Feature,no-auto-close,3rd party: OCC'
exempt-issue-labels: 'Priority: High,Priority: Critical,Blocker,Type: Feature,no-auto-close'
remove-stale-when-updated: true
ascending: true
stale-issue-message: |
@@ -110,7 +110,7 @@ jobs:
- name: '🧹 Tag & close inactive PRs'
id: inactive_pr
uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-issue-stale: -1
+3 -5
View File
@@ -28,15 +28,13 @@ jobs:
)
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
token: ${{ secrets.GH_TOKEN_FOR_CI_RUNNERS }}
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Create backport pull requests
uses: korthout/backport-action@01619ebc9a6e3f6820274221b9956b3e7365000a # v4.1.0
uses: korthout/backport-action@d07416681cab29bf2661702f925f020aaa962997 # v3.4.1
with:
# Inputs documented here: https://github.com/korthout/backport-action?tab=readme-ov-file#inputs
github_token: ${{ secrets.GH_TOKEN_FOR_CI_RUNNERS }}
github_token: ${{ github.token }}
github_workspace: ${{ github.workspace }}
# permit PRs with merge commits to be backported
+19 -112
View File
@@ -8,11 +8,6 @@ on:
permissions:
contents: write
actions: write
concurrency:
group: build-release-${{ github.event_name }}
cancel-in-progress: true
jobs:
upload_src:
@@ -21,19 +16,19 @@ jobs:
build_tag: ${{ steps.get_tag.outputs.build_tag }}
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Checkout Source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
- name: Get tag and create release if weekly
- name: get tag and create release if weekly
id: get_tag
shell: bash -l {0}
env:
@@ -43,38 +38,11 @@ jobs:
export BUILD_TAG="${{ github.event.release.tag_name }}"
else
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" \
-F .github/workflows/weekly-build-notes.md \
--prerelease || \
gh release view ${BUILD_TAG} > /dev/null # fail if it doesn't exist
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" -F .github/workflows/weekly-build-notes.md --prerelease || true
fi
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
echo "build_tag=${BUILD_TAG}" >> "$GITHUB_OUTPUT"
- name: Trigger notes updater workflow (only for weekly)
if: startsWith(steps.get_tag.outputs.build_tag, 'weekly-')
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
WEEKLY_TAG: ${{ steps.get_tag.outputs.build_tag }}
with:
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
// Reusable/dispatchable updater workflow file in .github/workflows/
const workflow_id = 'weekly-compare-link.yml';
// Use the default branch so the workflow file is available
const ref = (context.payload?.repository?.default_branch) || 'main';
const current_tag = process.env.WEEKLY_TAG || '';
await github.rest.actions.createWorkflowDispatch({
owner, repo, workflow_id, ref,
inputs: { current_tag }
});
core.info(`Dispatched ${workflow_id} on ${ref} with current_tag='${current_tag}'.`)
- name: Upload Source
id: upload_source
shell: bash -l {0}
@@ -102,9 +70,8 @@ jobs:
include:
- { target: linux-64, os: ubuntu-22.04 }
- { target: linux-arm64, os: ubuntu-22.04-arm }
- { target: osx-64, os: macos-15-intel, deploy_target: "10.13" }
- { target: osx-arm64, os: macos-latest, deploy_target: "11.0" }
- { target: osx-arm64, os: macos-latest, deploy_target: "15.0" }
- { target: osx-64, os: macos-15-intel }
- { target: osx-arm64, os: macos-latest }
- { target: win-64, os: windows-latest }
fail-fast: false
@@ -112,7 +79,7 @@ jobs:
environment: weekly-build
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -125,7 +92,7 @@ jobs:
remove-android: 'true' # (frees ~9 GB)
remove-cached-tools: 'true' # (frees ~8.3 GB)
- name: Set platform environment variables
- name: Set Platform Environment Variables
shell: bash -l {0}
env:
OPERATING_SYSTEM: ${{ runner.os }}
@@ -135,21 +102,21 @@ jobs:
echo 'RATTLER_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
fi
- name: Checkout source
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Checkout Source
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
ref: ${{ github.sha }}
fetch-depth: 2
fetch-tags: true
submodules: 'recursive'
- uses: prefix-dev/setup-pixi@a0af7a228712d6121d37aba47adf55c1332c9c2e # v0.9.4
- uses: prefix-dev/setup-pixi@82d477f15f3a381dbcc8adc1206ce643fe110fb7 # v0.9.3
with:
pixi-version: v0.59.0
cache: false
- name: Install the Apple certificate and provisioning profile
id: macos_get_cert
id: get_cert
if: runner.os == 'macOS'
env:
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
@@ -157,6 +124,7 @@ jobs:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
DEVELOPER_TEAM_ID: ${{ secrets.DEVELOPER_TEAM_ID }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
run: |
if [ -z "$BUILD_CERTIFICATE_BASE64" ]; then
@@ -192,81 +160,20 @@ jobs:
xcrun notarytool store-credentials "FreeCAD" --keychain "$KEYCHAIN_PATH" --apple-id "${APPLE_ID}" --password "${APP_SPECIFIC_PASSWORD}" --team-id "${DEVELOPER_TEAM_ID}"
- name: Setup .NET 10 SDK
if: runner.os == 'Windows'
uses: actions/setup-dotnet@baa11fbfe1d6520db94683bd5c7a3818018e4309 # v5.1.0
with:
dotnet-version: '10.0.103'
- name: Install sign tool
if: runner.os == 'Windows'
run: |
# NOTE: The sign tool is ONLY available as a prerelease package, they never issue real releases.
dotnet tool install --global --prerelease sign
echo "$env:USERPROFILE\.dotnet\tools" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
dotnet --info
sign --version
- name: Build packages
shell: bash
env:
GH_TOKEN: ${{ github.token }}
TARGET_PLATFORM: ${{ matrix.target }}
BUILD_TAG: ${{ needs.upload_src.outputs.build_tag }}
run: |
set -euo pipefail
if [[ "${{ runner.os }}" == "macOS" ]]; then
export MACOS_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}"
fi
python3 package/scripts/write_version_info.py ../freecad_version.txt
cd package/rattler-build
pixi install
- name: Azure login for Windows build code signing
id: azure_login
if: runner.os == 'Windows'
continue-on-error: true
uses: azure/login@a457da9ea143d694b1b9c7c869ebb04ebe844ef5 # v2.3.0
with:
creds: '{"clientId":"${{ secrets.AZURE_CLIENT_ID }}","clientSecret":"${{ secrets.AZURE_CLIENT_SECRET }}","subscriptionId":"${{ secrets.AZURE_SUBSCRIPTION_ID }}","tenantId":"${{ secrets.AZURE_TENANT_ID }}"}'
- name: Release packages with optional code-signing on Windows and macOS
- name: Build and Release Packages
shell: bash
env:
GH_TOKEN: ${{ github.token }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGN_RELEASE: ${{ steps.get_cert.outputs.has_cert }}
TARGET_PLATFORM: ${{ matrix.target }}
MAKE_INSTALLER: "true"
UPLOAD_RELEASE: "true"
BUILD_TAG: ${{ needs.upload_src.outputs.build_tag }}
run: |
set -euo pipefail
export MACOS_SIGN_RELEASE=0
export WINDOWS_SIGN_RELEASE=0
case "${RUNNER_OS}" in
Windows)
export WINDOWS_SIGN_RELEASE="${{ steps.azure_login.outcome }}"
export WINDOWS_AZURE_ENDPOINT="${{ vars.AZURE_TRUSTED_SIGNING_ENDPOINT }}"
export WINDOWS_AZURE_CERTIFICATE_PROFILE="${{ vars.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }}"
export WINDOWS_AZURE_SIGNING_ACCOUNT="${{ vars.AZURE_TRUSTED_SIGNING_ACCOUNT }}"
# For good measure, normalize the azure_login result to 1 or 0
if [[ "${WINDOWS_SIGN_RELEASE:-}" == "success" ]]; then
export WINDOWS_SIGN_RELEASE=1
else
export WINDOWS_SIGN_RELEASE=0
fi
;;
macOS)
export MACOS_SIGN_RELEASE="${{ steps.macos_get_cert.outputs.has_cert }}"
export MACOS_SIGNING_KEY_ID="${{ secrets.SIGNING_KEY_ID }}"
export MACOS_DEPLOYMENT_TARGET="${{ matrix.deploy_target }}"
;;
esac
python3 package/scripts/write_version_info.py ../freecad_version.txt
cd package/rattler-build
pixi install
pixi run -e package create_bundle
## Needed if running on a self-hosted runner:
+2 -2
View File
@@ -64,12 +64,12 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 2
submodules: recursive
+3 -3
View File
@@ -68,7 +68,7 @@ jobs:
# your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -84,7 +84,7 @@ jobs:
remove-docker-images: 'true' # (frees ~3.2 GB)
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
fetch-depth: 2
submodules: recursive
@@ -156,7 +156,7 @@ jobs:
sarif_file: sarif-results/cpp.sarif
- name: Upload loc as a Build Artifact
uses: actions/upload-artifact@v7.0.0
uses: actions/upload-artifact@v5
with:
name: sarif-results
path: sarif-results
+3 -3
View File
@@ -17,11 +17,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: 'Checkout Repository'
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: 'Dependency Review'
uses: actions/dependency-review-action@05fe4576374b728f0c523d6a13d64c25081e0803 # v4
uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4
+2 -10
View File
@@ -10,8 +10,6 @@ jobs:
trigger-copr-build:
environment: fedora-daily
env:
copr_login: ${{ secrets.COPR_LOGIN }}
copr_username: ${{ secrets.COPR_USERNAME }}
copr_token: ${{ secrets.COPR_TOKEN }}
runs-on: ubuntu-latest
container: quay.io/packit/packit
@@ -19,15 +17,9 @@ jobs:
- name: setup copr token
run: |
mkdir -p ~/.config
echo \
"[copr-cli]
login = $copr_login
username = $copr_username
token = $copr_token
copr_url = https://copr.fedorainfracloud.org
" > ~/.config/copr
echo "$copr_token" > ~/.config/copr
- name: checkout sources
uses: actions/checkout@v6
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 500
@@ -10,10 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
fetch-depth: 0
token: ${{ secrets.GH_TOKEN_FOR_CROWDIN_SYNC }}
- name: Install Qt ≥ 6.8
uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005
@@ -25,7 +24,7 @@ jobs:
arch: "linux_gcc_64"
- name: Setup Python & dependencies
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c
with:
python-version: "3.13"
- name: Install Python packages
@@ -46,8 +45,8 @@ jobs:
- name: Commit changes
run: |
git config --global user.name "freecad-gh-actions-translation-bot"
git config --global user.email "freecad-gh-actions-translation-bot@github.com"
git config --global user.name "github-actions"
git config --global user.email "github-actions@github.com"
git add src
git commit -m "Update translations from Crowdin" || echo "No changes to commit"
@@ -57,12 +56,12 @@ jobs:
git push origin update-crowdin-translations --force
- name: Create Pull Request
uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
with:
branch: update-crowdin-translations
title: "Update translations from Crowdin"
body: "Automatic Crowdin update."
token: ${{ secrets.GH_TOKEN_FOR_CROWDIN_SYNC }}
labels: "translations, automated"
delete-branch: true
add-paths: |
src
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -35,7 +35,7 @@ jobs:
echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"
- name: Run issue-metrics tool
uses: github/issue-metrics@6a35322ff89cee3e1a594d282c27eb34bffa9174 # v4.1.1
uses: github/issue-metrics@637a24e71b78bc10881e61972b19ea9ff736e14a # v3.25.2
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }}'
+1 -1
View File
@@ -21,7 +21,7 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -10,10 +10,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
with:
fetch-depth: 0
ref: releases/FreeCAD-1-1
- name: Install Qt ≥ 6.8
uses: jurplel/install-qt-action@d325aaf2a8baeeda41ad0b5d39f84a6af9bcf005
@@ -25,7 +24,7 @@ jobs:
arch: "linux_gcc_64"
- name: Setup Python & dependencies
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c
with:
python-version: "3.13"
- name: Install Python packages
+3 -3
View File
@@ -36,12 +36,12 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: "Checkout code"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
persist-credentials: false
@@ -68,7 +68,7 @@ jobs:
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: SARIF file
path: results.sarif
+7 -7
View File
@@ -75,7 +75,7 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -91,7 +91,7 @@ jobs:
fi
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Add GCC Problem Matcher
if: runner.os == 'Linux'
@@ -116,7 +116,7 @@ jobs:
mkdir -p ${{ env.reportdir }}
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
- uses: prefix-dev/setup-pixi@a0af7a228712d6121d37aba47adf55c1332c9c2e # v0.9.4
- uses: prefix-dev/setup-pixi@82d477f15f3a381dbcc8adc1206ce643fe110fb7 # v0.9.3
with:
pixi-version: v0.59.0
cache: false
@@ -124,7 +124,7 @@ jobs:
- name: Restore Compiler Cache
id: cache-restore
if: always()
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
@@ -209,14 +209,14 @@ jobs:
- name: Save Compiler Cache
id: cache-save
if: always()
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-${{ matrix.os }}-Logs
path: |
@@ -225,7 +225,7 @@ jobs:
- name: Upload report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
+11 -11
View File
@@ -22,9 +22,9 @@
# ***************************************************************************
# This is a build and test workflow for CI of FreeCAD.
# This workflow aims at building and testing FreeCAD on Ubuntu 24.04 using GCC.
# This workflow aims at building and testing FreeCAD on Ubuntu 22.04 using GCC.
name: Build Ubuntu 24.04
name: Build Ubuntu 22.04
on:
workflow_call:
@@ -47,7 +47,7 @@ on:
jobs:
Build:
runs-on: ubuntu-24.04
runs-on: ubuntu-22.04
continue-on-error: ${{ inputs.allowedToFail }}
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpfullversion -dumpversion" # default:mtime
@@ -77,12 +77,12 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checking out source code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: true
@@ -94,7 +94,7 @@ jobs:
sudo apt-get update -y -qq
sudo apt-get install -y -qq python3-pip
python3 -m pip install --upgrade pip
python3 -m pip install ifcopenshell==0.8.2 --break-system-packages
python3 -m pip install ifcopenshell==0.8.2
- name: Make needed directories, files and initializations
id: Init
@@ -116,7 +116,7 @@ jobs:
- name: Restore Compiler Cache
id: cache-restore
if: always()
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ steps.genCacheKey.outputs.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
@@ -131,7 +131,7 @@ jobs:
ccache -p
- name: Install cmake
uses: jwlawson/actions-setup-cmake@3a6cbe35ba64df7ca70c51365c4aff65db9a9037 # v2.1.1
uses: jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be # v2.0.2
with:
cmake-version: '3.31.6'
@@ -216,14 +216,14 @@ jobs:
- name: Save Compiler Cache
id: cache-save
if: always()
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ steps.genCacheKey.outputs.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
@@ -232,7 +232,7 @@ jobs:
- name: Upload report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
+8 -20
View File
@@ -24,27 +24,15 @@
name: Build Windows
on:
workflow_dispatch:
inputs:
artifactBasename:
description: "Base name for artifact"
required: false
type: string
default: "FreeCAD"
allowedToFail:
description: "whether a failed build is allowed"
required: false
type: boolean
default: false
workflow_call:
inputs:
artifactBasename:
required: true
type: string
required: true
allowedToFail:
required: false
default: false
type: boolean
required: false
outputs:
reportFile:
value: ${{ jobs.Build.outputs.reportFile }}
@@ -78,12 +66,12 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Checking out source code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: true
@@ -111,7 +99,7 @@ jobs:
- name: Restore compiler cache
id: cache-restore
if: always()
uses: actions/cache/restore@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
@@ -126,7 +114,7 @@ jobs:
. $env:ccachebindir\ccache -p
- name: Install cmake
uses: jwlawson/actions-setup-cmake@3a6cbe35ba64df7ca70c51365c4aff65db9a9037 # v2.1.1
uses: jwlawson/actions-setup-cmake@802fa1a2c4e212495c05bf94dba2704a92a472be # v2.0.2
with:
cmake-version: '3.31.6'
@@ -169,14 +157,14 @@ jobs:
- name: Save Compiler Cache
id: cache-save
if: always()
uses: actions/cache/save@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5.0.3
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ env.CCACHE_DIR }}
key: FC-${{ env.cacheKey }}-${{ github.ref }}-${{ github.run_id }}
- name: Upload logs
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
+4 -4
View File
@@ -198,12 +198,12 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: true
@@ -355,7 +355,7 @@ jobs:
- name: Upload logs and fixes
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
@@ -364,7 +364,7 @@ jobs:
- name: Upload report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
+4 -4
View File
@@ -81,7 +81,7 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -94,7 +94,7 @@ jobs:
touch ${{ env.logdir }}changedFiles.lst ${{ env.logdir }}changedCppFiles.lst ${{ env.logdir }}changedPythonFiles.lst
echo "reportFile=${{ env.reportfilename }}" >> $GITHUB_OUTPUT
- name: Check out code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: true
- name: Determine base and head SHA in case of PR
@@ -180,14 +180,14 @@ jobs:
echo "" >> $GITHUB_OUTPUT
- name: Upload logs
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ inputs.artifactBasename }}-Logs
path: |
${{ env.logdir }}
- name: Upload report
if: always()
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
name: ${{ env.reportfilename }}
path: |
+3 -3
View File
@@ -54,7 +54,7 @@ jobs:
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
with:
egress-policy: audit
@@ -62,7 +62,7 @@ jobs:
run: |
mkdir -p ${{ env.artifactsDownloadDir }}
- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
with:
path: ${{ env.artifactsDownloadDir }}
- name: Save input data to file
@@ -119,7 +119,7 @@ jobs:
cat report.md >> $GITHUB_STEP_SUMMARY
- name: Delete used artifacts
continue-on-error: true
uses: geekyeggo/delete-artifact@176a747ab7e287e3ff4787bf8a148716375ca118 # v6.0.0
uses: geekyeggo/delete-artifact@f275313e70c08f6120db482d7a6b98377786765b # v5.1.0
with:
name: |
${{ env.usedArtifacts }}
+1 -2
View File
@@ -1,7 +1,6 @@
> [!IMPORTANT]
> Bleeding edge FreeCAD development builds for testing bugfixes, regressions, and recently implemented features. Do not use in a production environment.
**Changes since last weekly:** <!--DIFF_LINK-->
### How-to use
@@ -9,7 +8,7 @@
2. Unpack the bundle to any folder on your system
3. Launch the application
- **Windows**
Run `\FreeCAD.exe` in the extracted directory
Run `\bin\FreeCAD.exe` in the extracted directory
- **macOS**
Launch `/FreeCAD.app` in the extracted directory
- **Linux**
-140
View File
@@ -1,140 +0,0 @@
name: Weekly compare link to release notes
on:
release:
types: [published] # run automatically when a (pre-)release is published
workflow_dispatch: # allow manual runs too
inputs:
current_tag:
description: "Weekly tag (e.g., weekly-2026.01.07). Leave empty to auto-detect latest weekly pre-release."
required: false
dry_run:
description: "Only compute; do not update the release body."
required: false
type: boolean
default: false
permissions:
contents: write # required to PATCH the release body
jobs:
update-notes:
runs-on: ubuntu-latest
steps:
- name: Inject compare link into weekly release notes
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
env:
# Pass manual inputs via env for convenience
CURRENT_TAG: ${{ github.event.inputs.current_tag }}
DRY_RUN: ${{ github.event.inputs.dry_run }}
PLACEHOLDER: "<!--DIFF_LINK-->"
with:
script: |
// Updates the current weekly release notes with a compare link to the previous weekly.
// Works for both release-published events and manual (workflow_dispatch) runs.
const owner = context.repo.owner;
const repo = context.repo.repo;
const rx = /^weekly-(\d{4})\.(\d{2})\.(\d{2})$/;
// Determine currentTag:
// 1) Manual input via workflow_dispatch (env.CURRENT_TAG)
// 2) Tag from release event payload
// 3) Fallback: newest weekly pre-release
let currentTag = process.env.CURRENT_TAG || (context.payload?.release?.tag_name) || null;
async function detectLatestWeeklyTag() {
const releases = await github.paginate(github.rest.repos.listReleases, { owner, repo, per_page: 100 });
const cand = releases.find(r => r.prerelease && typeof r.tag_name === 'string' && rx.test(r.tag_name));
return cand?.tag_name || null;
}
if (!currentTag) {
currentTag = await detectLatestWeeklyTag();
}
if (!currentTag || !rx.test(currentTag)) {
core.info(`No weekly tag detected or tag format mismatch ('${currentTag}'). Skipping.`);
return;
}
// Resolve the current release object
let curRel;
try {
const { data } = await github.rest.repos.getReleaseByTag({ owner, repo, tag: currentTag });
curRel = data;
} catch (e) {
core.setFailed(`No release for tag ${currentTag}: ${e.message}`);
return;
}
// If event is a normal release (not pre-release), skip automatically-run case;
// but allow manual override (manual runs can patch any weekly tag).
const isManual = context.eventName === 'workflow_dispatch';
if (!isManual && !curRel.prerelease) {
core.info('Current release is not a pre-release; skipping (auto run).');
return;
}
// Helpers
const toPrevWeeklyTag = (tag) => {
const [, y, m, d] = tag.match(rx);
const dt = new Date(Date.UTC(+y, +m - 1, +d));
const prev = new Date(dt.getTime() - 7 * 24 * 3600 * 1000); // minus 7 days
const iso = prev.toISOString().slice(0, 10); // YYYY-MM-DD
return `weekly-${iso.replace(/-/g, '.')}`; // weekly-YYYY.MM.DD
};
const ymdKey = (t) => t.replace(rx, '$1$2$3'); // YYYYMMDD
async function tagExists(tag) {
try {
await github.rest.git.getRef({ owner, repo, ref: `tags/${tag}` });
return true;
} catch {
return false;
}
}
// Compute previous weekly deterministically, then fall back if needed
let prevTag = toPrevWeeklyTag(currentTag);
if (!(await tagExists(prevTag))) {
core.info(`Computed previous tag ${prevTag} not found; scanning older weeklies...`);
const releases = await github.paginate(github.rest.repos.listReleases, { owner, repo, per_page: 100 });
const curKey = ymdKey(currentTag);
const older = releases
.filter(r => r.prerelease && typeof r.tag_name === 'string' && rx.test(r.tag_name))
.map(r => ({ tag: r.tag_name, key: ymdKey(r.tag_name) }))
.filter(x => x.key < curKey)
.sort((a, b) => b.key.localeCompare(a.key)); // newest older first
if (!older.length) {
core.info('No older weekly found; nothing to do.');
return;
}
prevTag = older[0].tag;
}
const compareUrl = `https://github.com/${owner}/${repo}/compare/${prevTag}...${currentTag}`;
const head = `**Changes since last weekly (${prevTag} → ${currentTag}):**\n${compareUrl}\n`;
if (process.env.DRY_RUN === 'true') {
core.info(`[DRY RUN] Would update release ${currentTag} with: ${compareUrl}`);
return;
}
// Idempotent body update
let body = curRel.body || '';
if (body.includes(compareUrl)) {
core.info('Compare URL already present; done.');
return;
}
const placeholder = process.env.PLACEHOLDER || '<!--DIFF_LINK-->';
if (body.includes(placeholder)) {
body = body.replace(placeholder, compareUrl);
} else if (/\*\*Changes since last weekly:/i.test(body)) {
body = body.replace(/\*\*Changes since last weekly:[^\n]*\n?/i, head + '\n');
} else {
body += (body.endsWith('\n') ? '\n' : '\n\n') + head;
}
await github.rest.repos.updateRelease({ owner, repo, release_id: curRel.id, body });
core.info(`Release notes updated with compare link: ${compareUrl}`);
+31 -32
View File
@@ -1,60 +1,59 @@
# file types to ignore
.*
!*.gitattributes
!*.gitignore
!/.packit.yaml
!/.clang-format
!/.clang-tidy
!/.git-blame-ignore-revs
!/.gitattributes
!/.github/
!/.gitignore
!/.gitmodules
!/.packit.yaml
!/.pre-commit-config.yaml
!/.pylintrc
!/.github/
!/contrib/.vscode/
*.clangd
!*.gitattributes
!*.gitignore
*.DS_Store
*.dep
*.exp
*.kdev4
*.pyc
*.obj
*.lib
*.pch
*.vcproj
*.kdev4
*.exp
*.dep
*.manifest
*.o
*.obj
*.orig
*.output
*.pch
*.pyc
*.vcproj
# Ignore .db files created by Qt Designer when editing .ui files
*.db
*qrc.depends
*~
qrc_*.cpp
BuildLog.htm
CMakeCache.txt
CMakeFiles/
CMakeLists.txt.user
Makefile
cmake_install.cmake
*~
CMakeFiles/
*qrc.depends
ui_*.h
moc_*.cpp
Makefile
CMakeCache.txt
CMakeLists.txt.user
config.h
install_manifest.txt
moc_*.cpp
qrc_*.cpp
ui_*.h
/.vscode/
/ALL_BUILD.dir/
/Mod/
/ZERO_CHECK.dir/
/bin/
/build-*/
/build/
/cmake-build*/
/conda/environment.yml
/ALL_BUILD.dir/
/doc/
/lib/
/src/Tools/offlinedoc/*.txt
/Mod/
/ZERO_CHECK.dir/
/build/
/build-*/
/cmake-build*/
/src/Tools/offlinedoc/localwiki/
/src/Tools/offlinedoc/*.txt
/conda/environment.yml
/.vscode/
OpenSCAD_rc.py
tags
/CMakeUserPresets.json
@@ -64,10 +63,10 @@ compile_commands.json
*.sublime-workspace
# crowdin file
files_to_translate.txt
src/Tools/freecad.zip
tsupdate_stderr.log
tsupdate_stdout.log
files_to_translate.txt
# pixi environments
*.egg-info
.pixi
*.egg-info
-1
View File
@@ -46,7 +46,6 @@ exclude: |
src/App/ExpressionParser.tab.h|
src/App/ExpressionParser.y|
src/App/lex.ExpressionParser.c|
src/Doc/ThirdPartyLibraries.html.cmake|
src/Gui/3Dconnexion/navlib|
src/Gui/QSint|
src/Gui/Quarter|
+1 -1
View File
@@ -398,7 +398,7 @@ indent-after-paren=4
indent-string=' '
# Maximum number of characters on a single line.
max-line-length=120
max-line-length=100
# Maximum number of lines in a module.
max-module-lines=1000
+4 -12
View File
@@ -51,11 +51,10 @@ endif()
project(FreeCAD)
set(PACKAGE_VERSION_MAJOR "1")
set(PACKAGE_VERSION_MINOR "2")
set(PACKAGE_VERSION_MINOR "1")
set(PACKAGE_VERSION_PATCH "0") # number of patch release (e.g. "4" for the 0.18.4 release)
set(PACKAGE_VERSION_SUFFIX "dev") # either "dev" for development snapshot or "" (empty string)
set(PACKAGE_VERSION_SUFFIX "rc1") # either "dev" for development snapshot or "" (empty string)
set(PACKAGE_BUILD_VERSION "0") # used when the same FreeCAD version will be re-released (for example using an updated LibPack)
string(TIMESTAMP PACKAGE_COPYRIGHT_YEAR "%Y")
set(PACKAGE_VERSION "${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}")
set(PACKAGE_STRING "${PROJECT_NAME} ${PACKAGE_VERSION}")
@@ -95,11 +94,8 @@ if(NOT FREECAD_LIBPACK_USE OR FREECAD_LIBPACK_CHECKFILE_CLBUNDLER OR FREECAD_LIB
endif(BUILD_GUI)
SetupSalomeSMESH()
if (BUILD_FEM_NETGEN)
set(NETGEN_DEFINITIONS -DNO_PARALLEL_THREADS -DOCCGEOMETRY)
if (NOT FREECAD_USE_EXTERNAL_SMESH)
find_package(NETGEN REQUIRED)
endif()
endif()
find_package(NETGEN)
endif(BUILD_FEM_NETGEN)
# not needed at the moment
#find_package(OpenCV REQUIRED)
SetupSwig()
@@ -119,10 +115,6 @@ if(NOT FREECAD_LIBPACK_USE OR FREECAD_LIBPACK_CHECKFILE_CLBUNDLER OR FREECAD_LIB
# SetupCoin3D can overwrite find_package(Boost) output so keep this after.
SetupBoost()
if(BUILD_BIM)
SetupLark()
endif()
endif()
if(BUILD_VR)
+1 -3
View File
@@ -62,9 +62,7 @@ The FreeCAD Contribution Process is expressed here with the following specific g
1. the PR description MUST contain proper attribution as the first line, for example: "This is work of XYZ cherry-picked from <link>";
2. all commits MUST have proper authorship, i.e. be authored by the original author and committed by the author of the PR;
3. if changes to cherry-picked commits are necessary they SHOULD be done as follow-up commits. If it is not possible to do so, then the modified commits MUST contain a `Co-Authored-By` trailer in their commit message.
14. Contributions must meet existing quality standards. Raw AI output is not accepted under any circumstances. AI may be used only as an assistive tool; in all cases, the resulting content must be reviewed, validated, and justifiable by the contributor. The contributor should be able to explain design and code decisions, answer reviewers questions, and ensure that AI use does not waste reviewers time during review. That being said, the use of AI is not recommended under any circumstances or in any manner.
15. The contributor provides reasonable assurance that the contribution does not infringe third-party copyrights or license terms.
16. A “Valid PR” is one which satisfies the above requirements.
14. A “Valid PR” is one which satisfies the above requirements.
## 6. Process
+8 -2
View File
@@ -56,8 +56,14 @@ Other options are described on the [wiki Download page](https://wiki.freecad.org
Compiling
---------
See the [Developers Handbook Getting Started](https://freecad.github.io/DevelopersHandbook/gettingstarted/)
for build instructions.
FreeCAD requires several dependencies to correctly compile for development and
production builds. The following pages contain updated build instructions for
their respective platforms:
- [Linux](https://wiki.freecad.org/Compile_on_Linux)
- [Windows](https://wiki.freecad.org/Compile_on_Windows)
- [macOS](https://wiki.freecad.org/Compile_on_MacOS)
- [MinGW](https://wiki.freecad.org/Compile_on_MinGW)
Reporting Issues
-53
View File
@@ -1,53 +0,0 @@
# - Find the lark library
# This module finds if lark is installed, and sets the following variables
# indicating where it is.
#
# LARK_FOUND - was lark found
# LARK_VERSION - the version of lark found as a string
# LARK_VERSION_MAJOR - the major version number of lark
# LARK_VERSION_MINOR - the minor version number of lark
# LARK_VERSION_PATCH - the patch version number of lark
include(FindPackageHandleStandardArgs)
if(Python3_EXECUTABLE)
message(STATUS "FindLark: Using Python3_EXECUTABLE = ${Python3_EXECUTABLE}")
# try to import lark into Python interpreter
execute_process(
COMMAND "${Python3_EXECUTABLE}" "-c"
"import lark; print(lark.__version__)"
RESULT_VARIABLE _LARK_SEARCH_SUCCESS
OUTPUT_VARIABLE LARK_VERSION
ERROR_VARIABLE _LARK_ERROR_VALUE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message(DEBUG "FindLark: Result = ${_LARK_SEARCH_SUCCESS}")
message(DEBUG "FindLark: Version = ${LARK_VERSION}")
message(DEBUG "FindLark: Error = ${_LARK_ERROR_VALUE}")
if(_LARK_SEARCH_SUCCESS MATCHES 0)
# extract version components
string(REGEX REPLACE "\\." ";" _LARK_VERSION_LIST ${LARK_VERSION})
list(LENGTH _LARK_VERSION_LIST _LARK_VERSION_LIST_LEN)
if(_LARK_VERSION_LIST_LEN GREATER_EQUAL 1)
list(GET _LARK_VERSION_LIST 0 LARK_VERSION_MAJOR)
endif()
if(_LARK_VERSION_LIST_LEN GREATER_EQUAL 2)
list(GET _LARK_VERSION_LIST 1 LARK_VERSION_MINOR)
endif()
if(_LARK_VERSION_LIST_LEN GREATER_EQUAL 3)
list(GET _LARK_VERSION_LIST 2 LARK_VERSION_PATCH)
endif()
else()
message(STATUS "The BIM workbench requires the lark python package / module to be installed")
endif()
else()
message(STATUS "FindLark: Python3_EXECUTABLE not set")
endif()
find_package_handle_standard_args(LARK
REQUIRED_VARS LARK_VERSION
VERSION_VAR LARK_VERSION
)
+5 -6
View File
@@ -13,15 +13,19 @@
# See also: http://git.salome-platform.org/gitweb/?p=NETGENPLUGIN_SRC.git;a=summary
find_package(Netgen CONFIG QUIET)
find_package(Netgen CONFIG)
IF (Netgen_FOUND)
set(NGLIB_INCLUDE_DIR ${NETGEN_INCLUDE_DIRS})
set(NGLIB_LIBRARIES nglib)
set(NETGEN_DEFINITIONS -DNO_PARALLEL_THREADS -DOCCGEOMETRY)
# for external smesh only the following two variables are needed:
set(NETGEN_FOUND True)
set(NETGEN_INCLUDE_DIRS ${NETGEN_INCLUDE_DIRS})
ELSE ()
SET(NETGEN_DEFINITIONS -DNO_PARALLEL_THREADS -DOCCGEOMETRY)
IF (WIN32)
FIND_PATH(NGLIB_INCLUDE_DIR NAMES nglib.h PATHS ${NETGEN_INCLUDEDIR})
SET(NETGEN_LIBS nglib mesh occ interface)
@@ -143,8 +147,3 @@ IF (Netgen_FOUND)
MESSAGE(STATUS "Found NETGEN version ${NETGEN_VERSION_MAJOR}.${NETGEN_VERSION_MINOR}, calculated: ${NETGEN_VERSION_C}")
LIST(APPEND NETGEN_DEFINITIONS -DNETGEN_VERSION=${NETGEN_VERSION_C})
ENDIF ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(NETGEN
REQUIRED_VARS NGLIB_INCLUDE_DIR NGLIB_LIBRARIES NETGEN_INCLUDE_DIRS
VERSION_VAR NETGEN_VERSION)
+9 -41
View File
@@ -1,11 +1,7 @@
# The Qt for Python project officially recommends using pip to install PySide,
# so we expect to find PySide in the site-packages directory.
# The library will be called "PySide6.abi3.*", and there will
# be an "include" directory inside the site-packages/PySide6.
# Over time some distros may provide custom versions, so we also support
# using a more normal cMake find_package() call
include(FindPackageHandleStandardArgs)
# The Qt for Python project officially recommends using pip to install PySide, so we expect to find PySide in the
# site-packages directory. The library will be called "PySide6.abi3.*", and there will be an "include" directory inside
# the site-packages/PySide6. Over time some distros may provide custom versions, so we also support using a more normal
# cMake find_package() call
find_package(PySide6 CONFIG QUIET)
@@ -13,40 +9,12 @@ if(NOT PySide6_FOUND)
find_pip_package(PySide6)
endif()
if(PySide6_FOUND)
# verify PySide6 version matches Qt6 version (major.minor)
if(PySide6_VERSION AND Qt6_VERSION)
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)" _qt6_major_minor "${Qt6_VERSION}")
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)" _pyside6_major_minor "${PySide6_VERSION}")
if(NOT PySide6_INCLUDE_DIRS AND TARGET PySide6::pyside6)
get_property(PySide6_INCLUDE_DIRS TARGET PySide6::pyside6 PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
endif()
message(STATUS "Qt version: ${Qt6_VERSION}")
message(STATUS "PySide version: ${PySide6_VERSION}")
if(NOT _qt6_major_minor STREQUAL _pyside6_major_minor)
message(FATAL_ERROR
" --------------------------------------------------------
Qt/PySide version mismatch!
cmake found Qt: ${Qt6_VERSION}
cmake found PySide: ${PySide6_VERSION}
major.minor versions of Qt and PySide must match to avoid errors.
Ensure CMAKE_PREFIX_PATH points to matching Qt and PySide6 installations.
--------------------------------------------------------"
)
endif()
message(STATUS "PySide/Qt version check passed (${_pyside6_major_minor})")
endif()
if(NOT PySide6_INCLUDE_DIRS AND TARGET PySide6::pyside6)
get_property(PySide6_INCLUDE_DIRS TARGET PySide6::pyside6 PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
endif()
find_package_handle_standard_args(PySide6
REQUIRED_VARS PySide6_INCLUDE_DIRS
VERSION_VAR PySide6_VERSION
)
# Also provide the old-style variables so we don't have to update everything yet
# Also provide the old-style variables so we don't have to update everything yet
if (PySide6_FOUND)
set(PYSIDE_INCLUDE_DIR ${PySide6_INCLUDE_DIRS})
set(PYSIDE_LIBRARY ${PySide6_LIBRARIES})
set(PYSIDE_FOUND TRUE)
+6 -6
View File
@@ -3,11 +3,11 @@
# PYSIDE_RCC_EXECUTABLE - Location of PYSIDE6RCC executable
# PYSIDE_TOOLS_FOUND - PYSIDE6 utilities found.
if(TARGET Qt6::uic)
if (TARGET Qt6::uic)
get_target_property(PYSIDE6_UIC_EXECUTABLE Qt6::uic LOCATION)
set(UICOPTIONS "--generator=python")
endif()
if(TARGET Qt6::rcc)
if (TARGET Qt6::rcc)
get_target_property(PYSIDE6_RCC_EXECUTABLE Qt6::rcc LOCATION)
set(RCCOPTIONS "--generator=python" "--compress-algo=zlib" "--compress=1")
endif()
@@ -18,15 +18,15 @@ set(PySideTools_VERSION 6)
if(PYSIDE_RCC_EXECUTABLE AND PYSIDE_UIC_EXECUTABLE)
set(PYSIDE_TOOLS_FOUND TRUE)
if(NOT PYSIDE6Tools_FIND_QUIETLY)
if (NOT PYSIDE6Tools_FIND_QUIETLY)
message(STATUS "Found PYSIDE6 tools: ${PYSIDE_UIC_EXECUTABLE}, ${PYSIDE_RCC_EXECUTABLE}")
endif()
endif ()
else()
if(PYSIDE6Tools_FIND_REQUIRED)
message(FATAL_ERROR "PYSIDE6 tools could not be found, but are required.")
else()
if(NOT PYSIDE6Tools_FIND_QUIETLY)
if (NOT PYSIDE6Tools_FIND_QUIETLY)
message(STATUS "PYSIDE6 tools: not found.")
endif()
endif ()
endif()
endif()
+2 -2
View File
@@ -10,14 +10,14 @@ if(NOT Shiboken6_FOUND)
endif()
if(NOT Shiboken6_INCLUDE_DIR)
find_pip_package(Shiboken6)
if(Shiboken6_FOUND)
if (Shiboken6_FOUND)
set(SHIBOKEN_LIBRARY ${Shiboken6_LIBRARIES})
set(SHIBOKEN_MAJOR_VERSION 6)
set(SHIBOKEN_FOUND ON)
endif()
# The include directory we actually want is part of shiboken6-generator
find_pip_package(shiboken6_generator)
if(shiboken6_generator_FOUND)
if (shiboken6_generator_FOUND)
set(SHIBOKEN_INCLUDE_DIR ${shiboken6_generator_INCLUDE_DIRS})
endif()
endif()
@@ -221,9 +221,6 @@ macro(PrintFinalReport)
conditional(fmt fmt_FOUND "Sources downloaded to ${fmt_SOURCE_DIR}" "${fmt_VERSION}")
conditional(yaml-cpp yaml-cpp_FOUND "not found" "${yaml-cpp_VERSION}")
conditional(Vtk VTK_FOUND "not found" ${VTK_VERSION})
if(BUILD_BIM)
conditional(Lark LARK_FOUND "not found" "${LARK_VERSION}")
endif()
section_end()
-16
View File
@@ -7,22 +7,6 @@ macro(SetupFreetype)
message("===============================================================\n"
"FreeType2 not found. Part module will lack of makeWireString().\n"
"===============================================================\n")
else()
# find_package(harfbuzz CONFIG) fails on windows
# lets do it the complicated way instead.
find_path(HARFBUZZ_INCLUDE_DIR hb.h PATH_SUFFIXES harfbuzz)
find_library(HARFBUZZ_LIBRARY NAMES harfbuzz)
if(HARFBUZZ_INCLUDE_DIR AND HARFBUZZ_LIBRARY)
if(NOT TARGET harfbuzz::harfbuzz)
add_library(harfbuzz::harfbuzz UNKNOWN IMPORTED)
set_target_properties(harfbuzz::harfbuzz PROPERTIES
IMPORTED_LOCATION "${HARFBUZZ_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${HARFBUZZ_INCLUDE_DIR}"
)
endif()
else()
message(FATAL_ERROR "HarfBuzz not found")
endif()
endif(NOT FREETYPE_FOUND)
endif(FREECAD_USE_FREETYPE)
-7
View File
@@ -1,7 +0,0 @@
macro(SetupLark)
# ------------------------------ Lark ------------------------------
find_package(LARK MODULE REQUIRED)
message(STATUS "Found Lark: version ${LARK_VERSION}")
endmacro()
+10 -23
View File
@@ -103,33 +103,20 @@ macro(SetupSalomeSMESH)
set(HDF5_VARIANT "hdf5-serial")
else()
message(STATUS "We guess that libmed was built using hdf5-openmpi version")
set(HDF5_VARIANT "hdf5-openmpi;hdf5_openmpi")
set(HDF5_VARIANT "hdf5-openmpi")
set(HDF5_PREFER_PARALLEL TRUE) # if pkg-config fails, find_package(HDF5) needs this
endif()
pkg_search_module(PCHDF5 ${HDF5_VARIANT})
if(NOT PCHDF5_FOUND)
pkg_search_module(HDF5 ${HDF5_VARIANT})
if(NOT HDF5_FOUND)
find_package(HDF5 REQUIRED)
else()
add_compile_options(${PCHDF5_CFLAGS})
link_directories(${PCHDF5_LIBRARY_DIRS})
link_libraries(${PCHDF5_LIBRARIES})
# workaround to define include dir from PCHDF5_CFLAGS (pkg-config PCHDF5_INCLUDEDIR is only filled since hdf5 1.14.6)
set(hdf5_include_path "")
foreach(flag IN LISTS PCHDF5_CFLAGS)
if(flag MATCHES "^-I")
string(REGEX REPLACE "^-I[ ]*" "" flag "${flag}")
list(APPEND hdf5_include_path "${flag}")
endif()
endforeach()
set(_save_INC CMAKE_REQUIRED_INCLUDES)
set(CMAKE_REQUIRED_INCLUDES ${hdf5_include_path})
check_include_file_cxx(hdf5.h HDF5_HEAD_FOUND)
set(CMAKE_REQUIRED_INCLUDES ${_save_INC})
if(NOT HDF5_HEAD_FOUND)
message( FATAL_ERROR "hdf5.h was not found (tested pkg-config ${HDF5_VARIANT}, suggested header location was '${hdf5_include_path}').")
endif()
add_compile_options(${HDF5_CFLAGS})
link_directories(${HDF5_LIBRARY_DIRS})
link_libraries(${HDF5_LIBRARIES})
endif()
check_include_file_cxx(hdf5.h HDF5_FOUND)
if(NOT HDF5_FOUND)
message( FATAL_ERROR "hdf5.h was not found.")
endif()
# Med Fichier can require MPI
@@ -7,7 +7,7 @@ macro(SetupShibokenAndPyside)
if(DEFINED MACPORTS_PREFIX)
find_package(Shiboken REQUIRED HINTS "${PYTHON_LIBRARY_DIR}/cmake")
find_package(PySide REQUIRED HINTS "${PYTHON_LIBRARY_DIR}/cmake")
endif()
endif(DEFINED MACPORTS_PREFIX)
if(FREECAD_QT_MAJOR_VERSION EQUAL 5)
set(SHIBOKEN_MAJOR_VERSION 2)
@@ -21,16 +21,16 @@ macro(SetupShibokenAndPyside)
# Shiboken2Config.cmake may explicitly set CMAKE_BUILD_TYPE to Release which causes
# CMake to fail to create Makefiles for a debug build.
# So as a workaround we save and restore the value after checking for Shiboken2.
set(SAVE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
set (SAVE_BUILD_TYPE ${CMAKE_BUILD_TYPE})
find_package(Shiboken${SHIBOKEN_MAJOR_VERSION} QUIET)
set(CMAKE_BUILD_TYPE ${SAVE_BUILD_TYPE})
if(Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
set (CMAKE_BUILD_TYPE ${SAVE_BUILD_TYPE})
if (Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
# Shiboken config file was found but it may use the wrong Python version
# Try to get the matching config suffix and repeat finding the package
set(SHIBOKEN_PATTERN .cpython-${Python3_VERSION_MAJOR}${Python3_VERSION_MINOR})
file(GLOB SHIBOKEN_CONFIG "${Shiboken${SHIBOKEN_MAJOR_VERSION}_DIR}/Shiboken${SHIBOKEN_MAJOR_VERSION}Config${SHIBOKEN_PATTERN}*.cmake")
if(SHIBOKEN_CONFIG)
if (SHIBOKEN_CONFIG)
get_filename_component(SHIBOKEN_CONFIG_SUFFIX ${SHIBOKEN_CONFIG} NAME)
string(SUBSTRING ${SHIBOKEN_CONFIG_SUFFIX} 15 -1 SHIBOKEN_CONFIG_SUFFIX)
string(REPLACE ".cmake" "" PYTHON_CONFIG_SUFFIX ${SHIBOKEN_CONFIG_SUFFIX})
@@ -47,11 +47,11 @@ macro(SetupShibokenAndPyside)
# to dance to be compatible with the old (<5.12) and the new versions (>=5.12)
if(NOT SHIBOKEN_INCLUDE_DIR AND TARGET Shiboken${SHIBOKEN_MAJOR_VERSION}::libshiboken)
get_property(SHIBOKEN_INCLUDE_DIR TARGET Shiboken${SHIBOKEN_MAJOR_VERSION}::libshiboken PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
endif()
endif(NOT SHIBOKEN_INCLUDE_DIR AND TARGET Shiboken${SHIBOKEN_MAJOR_VERSION}::libshiboken)
if(NOT SHIBOKEN_INCLUDE_DIR)
find_pip_package(Shiboken${SHIBOKEN_MAJOR_VERSION})
if(Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
if (Shiboken${SHIBOKEN_MAJOR_VERSION}_FOUND)
set(SHIBOKEN_INCLUDE_DIR ${Shiboken${SHIBOKEN_MAJOR_VERSION}_INCLUDE_DIRS})
set(SHIBOKEN_LIBRARY ${Shiboken${SHIBOKEN_MAJOR_VERSION}_LIBRARIES})
endif()
@@ -63,11 +63,11 @@ macro(SetupShibokenAndPyside)
# Our internal FindPySide6.cmake file already provides these for PySide6
if(NOT PYSIDE_INCLUDE_DIR AND TARGET PySide${PYSIDE_MAJOR_VERSION}::pyside${PYSIDE_MAJOR_VERSION})
get_property(PYSIDE_INCLUDE_DIR TARGET PySide${PYSIDE_MAJOR_VERSION}::pyside${PYSIDE_MAJOR_VERSION} PROPERTY INTERFACE_INCLUDE_DIRECTORIES)
endif()
endif(NOT PYSIDE_INCLUDE_DIR AND TARGET PySide${PYSIDE_MAJOR_VERSION}::pyside${PYSIDE_MAJOR_VERSION})
if(NOT PYSIDE_INCLUDE_DIR)
find_pip_package(PySide${PYSIDE_MAJOR_VERSION})
if(PySide${PYSIDE_MAJOR_VERSION}_FOUND)
if (PySide${PYSIDE_MAJOR_VERSION}_FOUND)
set(PYSIDE_INCLUDE_DIR ${PySide${PYSIDE_MAJOR_VERSION}_INCLUDE_DIRS})
set(PYSIDE_LIBRARY ${PySide${PYSIDE_MAJOR_VERSION}_LIBRARIES})
endif()
@@ -175,22 +175,22 @@ macro(SetupShibokenAndPyside)
message(STATUS "PySide ${PySide_VERSION} Python module found at ${PRINT_OUTPUT}.\n")
endif()
endmacro()
endmacro(SetupShibokenAndPyside)
macro(PYSIDE_WRAP_RC outfiles)
if(NOT PYSIDE_RCC_EXECUTABLE)
MACRO(PYSIDE_WRAP_RC outfiles)
if (NOT PYSIDE_RCC_EXECUTABLE)
message(FATAL_ERROR "Qt rcc is required for generating ${ARGN}")
endif()
foreach(it ${ARGN})
get_filename_component(outfile ${it} NAME_WE)
get_filename_component(infile ${it} ABSOLUTE)
set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}_rc.py")
FOREACH(it ${ARGN})
GET_FILENAME_COMPONENT(outfile ${it} NAME_WE)
GET_FILENAME_COMPONENT(infile ${it} ABSOLUTE)
SET(outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}_rc.py")
#ADD_CUSTOM_TARGET(${it} ALL
# DEPENDS ${outfile}
#)
if(WIN32 OR APPLE)
add_custom_command(OUTPUT ${outfile}
ADD_CUSTOM_COMMAND(OUTPUT ${outfile}
COMMAND ${PYSIDE_RCC_EXECUTABLE} ${RCCOPTIONS} ${infile} -o ${outfile}
MAIN_DEPENDENCY ${infile}
)
@@ -198,7 +198,7 @@ macro(PYSIDE_WRAP_RC outfiles)
# Especially on Open Build Service we don't want changing date like
# pyside-rcc generates in comments at beginning, which is why
# we follow the tool command with in-place sed.
add_custom_command(OUTPUT "${outfile}"
ADD_CUSTOM_COMMAND(OUTPUT "${outfile}"
COMMAND "${PYSIDE_RCC_EXECUTABLE}" ${RCCOPTIONS} "${infile}" ${PY_ATTRIBUTE} -o "${outfile}"
# The line below sometimes catches unwanted lines too - but there is no date in the file
# anymore with Qt5 RCC, so commenting it out for now...
@@ -207,5 +207,5 @@ macro(PYSIDE_WRAP_RC outfiles)
)
endif()
list(APPEND ${outfiles} ${outfile})
endforeach()
endmacro()
ENDFOREACH(it)
ENDMACRO (PYSIDE_WRAP_RC)
+8 -156
View File
@@ -1,160 +1,12 @@
macro(SetupSwig)
# -------------------------------- Swig ----------------------------------
#-------------------------------- Swig ----------------------------------
find_package(SWIG)
# force cmake to re-search for SWIG when CMAKE_PREFIX_PATH changes
unset(SWIG_EXECUTABLE CACHE)
unset(SWIG_DIR CACHE)
unset(SWIG_VERSION CACHE)
unset(SWIG_FOUND CACHE)
if (NOT SWIG_FOUND)
message("=====================================================\n"
"SWIG not found, will not build SWIG binding for pivy.\n"
"=====================================================\n")
endif(NOT SWIG_FOUND)
if(BUILD_SKETCHER)
# SWIG is required for sketcher WB (use QUIET to provide custom error message)
find_package(SWIG QUIET)
if(NOT SWIG_FOUND)
message(FATAL_ERROR
"-----------------------------------------------------\n"
"SWIG not found, swig & pivy required for sketcher WB.\n"
"-----------------------------------------------------\n")
# do not continue with check if swig not found
return()
endif()
# check swig/pivy runtime compatibility
message(STATUS "checking SWIG/Pivy runtime compatibility...")
# get SWIG's runtime version using -external-runtime flag
execute_process(
COMMAND ${SWIG_EXECUTABLE} -python -external-runtime "${CMAKE_BINARY_DIR}/swig_runtime_check.h"
RESULT_VARIABLE SWIG_EXTERNAL_RUNTIME_RESULT
ERROR_VARIABLE SWIG_EXTERNAL_RUNTIME_ERROR
ERROR_QUIET
)
# NOTE: only print the below output if using `cmake --log-level=DEBUG`
message(DEBUG "SWIG external-runtime result: ${SWIG_EXTERNAL_RUNTIME_RESULT}")
message(DEBUG "SWIG external-runtime error: ${SWIG_EXTERNAL_RUNTIME_ERROR}")
message(STATUS "Looking for: ${CMAKE_BINARY_DIR}/swig_runtime_check.h")
if(EXISTS "${CMAKE_BINARY_DIR}/swig_runtime_check.h")
message(STATUS "File exists: YES")
else()
message(STATUS "File exists: NO")
endif()
if(EXISTS "${CMAKE_BINARY_DIR}/swig_runtime_check.h")
file(STRINGS "${CMAKE_BINARY_DIR}/swig_runtime_check.h"
SWIG_RUNTIME_VERSION_LINE
REGEX "^#define SWIG_RUNTIME_VERSION")
message(STATUS "SWIG_RUNTIME_VERSION_LINE: ${SWIG_RUNTIME_VERSION_LINE}")
if(SWIG_RUNTIME_VERSION_LINE)
# extract the version number (it's in quotes: "5")
string(REGEX MATCH "\"([0-9]+)\"" _ "${SWIG_RUNTIME_VERSION_LINE}")
set(SWIG_RUNTIME_VERSION "${CMAKE_MATCH_1}")
message(STATUS "Extracted SWIG_RUNTIME_VERSION: ${SWIG_RUNTIME_VERSION}")
endif()
file(REMOVE "${CMAKE_BINARY_DIR}/swig_runtime_check.h")
else()
message(STATUS "swig_runtime_check.h not found!")
endif()
# extract pivy's SWIG runtime version from the compiled module
# NOTE: python code can not be indented
set(PYTHON_CHECK_PIVY_RUNTIME [=[
import sys
import os
import re
try:
import pivy
pivy_dir = os.path.dirname(pivy.__file__)
print(f'DEBUG:pivy_dir={pivy_dir}', file=sys.stderr)
print(f'DEBUG:files={os.listdir(pivy_dir)}', file=sys.stderr)
pivy_path = None
# Look for _coin module with any extension (.so on Unix, .pyd on Windows)
for f in os.listdir(pivy_dir):
print(f'DEBUG:checking={f}', file=sys.stderr)
if f.startswith('_coin') and (f.endswith('.so') or f.endswith('.pyd')):
pivy_path = os.path.join(pivy_dir, f)
break
print(f'DEBUG:pivy_path={pivy_path}', file=sys.stderr)
if pivy_path and os.path.exists(pivy_path):
with open(pivy_path, 'rb') as f:
content = f.read().decode('latin-1', errors='ignore')
print(f'DEBUG:content_len={len(content)}', file=sys.stderr)
# Use regex to find swig_runtime_data followed by a number
match = re.search(r'swig_runtime_data(\d+)', content)
print(f'DEBUG:match={match}', file=sys.stderr)
if match:
print(match.group(1))
except ImportError as e:
print(f'DEBUG:import_error={e}', file=sys.stderr)
print('ERROR_IMPORT')
except Exception as e:
print(f'DEBUG:exception={e}', file=sys.stderr)
]=])
execute_process(
COMMAND ${Python3_EXECUTABLE} -c "${PYTHON_CHECK_PIVY_RUNTIME}"
OUTPUT_VARIABLE PIVY_RUNTIME_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE PIVY_DEBUG_OUTPUT
TIMEOUT 10
)
message(DEBUG "Pivy debug output: ${PIVY_DEBUG_OUTPUT}")
# Handle errors and compare versions
if(PIVY_RUNTIME_VERSION STREQUAL "ERROR_IMPORT")
message(WARNING
"Could not import pivy to check SWIG compatibility.\n"
"Proceeding without SWIG version check."
)
elseif(SWIG_RUNTIME_VERSION AND PIVY_RUNTIME_VERSION)
if(NOT SWIG_RUNTIME_VERSION STREQUAL PIVY_RUNTIME_VERSION)
message(FATAL_ERROR
" --------------------------------------------------------
SWIG / PIVY RUNTIME VERSION MISMATCH DETECTED!
SWIG runtime API version: ${SWIG_RUNTIME_VERSION}
Pivy runtime API version: ${PIVY_RUNTIME_VERSION}
These must match for compatibility.
This will cause runtime errors: 'No SWIG wrapped library loaded'
swig v4.4.x is not compatible with pivy built with swig <=4.3.x
FIX: Install a SWIG version that uses runtime ${PIVY_RUNTIME_VERSION}
or rebuild Pivy with your current SWIG ${SWIG_VERSION}.
--------------------------------------------------------"
)
else()
message(STATUS "SWIG/Pivy runtime compatibility: PASSED")
message(STATUS "swig runtime API version: ${SWIG_RUNTIME_VERSION}")
message(STATUS "pivy runtime API version: ${PIVY_RUNTIME_VERSION}")
message(STATUS "swig binary version building freecad: ${SWIG_VERSION}")
endif()
else()
if(NOT SWIG_RUNTIME_VERSION)
message(WARNING "Could not determine SWIG runtime version")
endif()
if(NOT PIVY_RUNTIME_VERSION)
message(WARNING "Could not determine Pivy runtime version")
endif()
message(WARNING "Proceeding without SWIG/Pivy compatibility check.")
endif()
endif()
endmacro()
endmacro(SetupSwig)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -3
View File
@@ -5,7 +5,7 @@
\pard\nowidctlpar\hyphpar0\sa140\sl276\slmult1\par
\pard\nowidctlpar\hyphpar0\sl276\slmult1\b\fs28 Third-party libraries licenses\b0\fs24\par
\pard\nowidctlpar\hyphpar0\sa140\sl276\slmult1\par
\pard\nowidctlpar\hyphpar0\sl276\slmult1\fs18 The different libraries used in FreeCAD and their respective licenses are described on the\line\fs24{\field{\*\fldinst{HYPERLINK "https://www.freecad.org/wiki/Third_Party_Libraries" }}{\fldrslt{\cf2\ul\fs18 Third Party Libraries wiki page}}}\cf0\ulnone\f0\fs18 .\fs24\par
\pard\nowidctlpar\hyphpar0\sl276\slmult1\fs18 The different libraries used in FreeCAD and their respective licenses are described on the\line\fs24{\field{\*\fldinst{HYPERLINK "https://www.freecadweb.org/wiki/Third_Party_Libraries" }}{\fldrslt{\cf2\ul\fs18 Third Party Libraries wiki page}}}\cf0\ulnone\f0\fs18 .\fs24\par
\par
\pard\noline\nowidctlpar\hyphpar0\sa283\fs12\par
\pard\nowidctlpar\hyphpar0\sl276\slmult1\b\fs18 GNU LIBRARY GENERAL PUBLIC LICENSE\b0\fs24\par
@@ -81,5 +81,4 @@
\pard\nowidctlpar\hyphpar0\sb240\sa180\sl276\slmult1\b\fs24 3D Mouse Support\b0\par
\pard\nowidctlpar\hyphpar0\sb180\sa180\sl276\slmult1\fs18 Development tools and related technology provided under license from 3Dconnexion.(c) 1992 - 2012 3Dconnexion. All rights reserved\fs12\par
}
+5 -1
View File
@@ -34,7 +34,11 @@ These typically need to be modified for each FreeCAD release
#--------------------------------
# get version info from freecadcmd
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode $\"${__FILEDIR__}\write_version_nsh.py$\"" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"import datetime; print(f'!define COPYRIGHT_YEAR {datetime.date.today().year}')$\">${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_MAJOR \$\"{App.Version()[0]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_MINOR \$\"{App.Version()[1]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_PATCH \$\"{App.Version()[2]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_REVISION \$\"{App.Version()[3].split()[0]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
!include "${__FILEDIR__}\version.nsh"
!delfile "${__FILEDIR__}\version.nsh"
+4 -5
View File
@@ -7,7 +7,7 @@ Language: English
${LangFileString} TEXT_INSTALL_CURRENTUSER "(Installed for Current User)"
${LangFileString} TEXT_WELCOME "This wizard will guide you through the installation of $(^NameDA). $\r$\n\
${LangFileString} TEXT_WELCOME "This wizard will guide you through the installation of $(^NameDA), $\r$\n\
$\r$\n\
$_CLICK"
@@ -64,8 +64,7 @@ ${LangFileString} SecUnPreferencesDescription 'Deletes FreeCAD$\'s configuration
$AppSuff\$\r$\n\
${APP_DIR_USERDATA}$\")$\r$\n\
for you or for all users (if you are admin).'
${LangFileString} DialogUnPreferences 'You chose to delete the FreeCAD user configuration.$\r$\n\
This will also delete all installed FreeCAD addons, and will affect the$\r$\n\
preferences for all versions of FreeCAD.$\r$\n\
Are you sure you want to proceed?'
${LangFileString} DialogUnPreferences 'You chose to delete the FreeCADs user configuration.$\r$\n\
This will also delete all installed FreeCAD addons.$\r$\n\
Do you agree with this?'
${LangFileString} SecUnProgramFilesDescription "Uninstall FreeCAD and all of its components."
@@ -1,20 +0,0 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# this script is meant to be called by nsis installer scripts, it gets version information
# from freecad and writes version.nsh file in the directory the script is located at
import FreeCAD
import datetime
import os
filepath=os.path.join(os.path.dirname(os.path.abspath(__file__)),"version.nsh")
v=FreeCAD.Version()
content=f'''\
!define COPYRIGHT_YEAR {datetime.date.today().year}
!define APP_VERSION_MAJOR "{v[0]}"
!define APP_VERSION_MINOR "{v[1]}"
!define APP_VERSION_PATCH "{v[2]}"
!define APP_VERSION_REVISION "{v[3].split()[0]}"
'''
with open(filepath, "w", encoding="utf-8") as file:
file.writelines(content)
+4 -4
View File
@@ -16,7 +16,7 @@
Name: freecad
Epoch: 1
Version: 1.1.0~dev
Version: 1.1.0~rc1
Release: 1%{?dist}
Summary: A general purpose 3D CAD modeler
@@ -29,7 +29,7 @@ Source0: freecad-sources.tar.gz
# Maintainers: keep this list of plugins up to date
# List plugins in %%{_libdir}/%%{name}/lib, less '.so' and 'Gui.so', here
%global plugins AssemblyApp AssemblyGui CAMSimulator DraftUtils Fem FreeCAD Import Inspection MatGui Materials Measure Mesh MeshPart Part PartDesignGui Path PathApp PathSimulator Points QtUnitGui ReverseEngineering Robot Sketcher Spreadsheet Start Surface TechDraw Web _PartDesign area flatmesh libDriver libDriverDAT libDriverSTL libDriverUNV libE57Format libMEFISTO2 libSMDS libSMESH libSMESHDS libStdMeshers libarea-native tsp_solver
%global plugins AssemblyApp AssemblyGui CAMSimulator DraftUtils Fem FreeCAD Import Inspection MatGui Materials Measure Mesh MeshPart Part PartDesignGui Path PathApp PathSimulator Points QtUnitGui ReverseEngineering Robot Sketcher Spreadsheet Start Surface TechDraw Web _PartDesign area flatmesh libDriver libDriverDAT libDriverSTL libDriverUNV libE57Format libMEFISTO2 libSMDS libSMESH libSMESHDS libStdMeshers libarea-native
%global exported_libs libOndselSolver
@@ -44,14 +44,14 @@ Source0: freecad-sources.tar.gz
# Utilities
BuildRequires: cmake gcc-c++ gettext doxygen swig graphviz gcc-gfortran desktop-file-utils tbb-devel ninja-build strace
%if %{with tests}
BuildRequires: xorg-x11-server-Xvfb python3-typing-extensions
BuildRequires: xorg-x11-server-Xvfb python3-typing-extensions
%if %{without bundled_gtest}
BuildRequires: gtest-devel gmock-devel
%endif
%endif
# Development Libraries
BuildRequires:boost-devel Coin4-devel eigen3-devel freeimage-devel fmt-devel libglvnd-devel libicu-devel libspnav-devel libXmu-devel med-devel mesa-libEGL-devel mesa-libGLU-devel netgen-mesher-devel netgen-mesher-devel-private opencascade-devel openmpi-devel python3 python3-devel python3-lark python3-matplotlib python3-pivy python3-pybind11 python3-pyside6-devel python3-shiboken6-devel pyside6-tools qt6-qttools-static qt6-qtsvg-devel vtk-devel xerces-c-devel yaml-cpp-devel
BuildRequires:boost-devel Coin4-devel eigen3-devel freeimage-devel fmt-devel libglvnd-devel libicu-devel libspnav-devel libXmu-devel med-devel mesa-libEGL-devel mesa-libGLU-devel netgen-mesher-devel netgen-mesher-devel-private opencascade-devel openmpi-devel python3 python3-devel python3-matplotlib python3-pivy python3-pybind11 python3-pyside6-devel python3-shiboken6-devel pyside6-tools qt6-qttools-static qt6-qtsvg-devel vtk-devel xerces-c-devel yaml-cpp-devel
#pcl-devel
%if %{without bundled_smesh}
BuildRequires: smesh-devel
+2
View File
@@ -57,6 +57,8 @@
test: xvfb-run /usr/bin/FreeCAD -t TestPartGui
/TestPartDesignGui :
test: xvfb-run /usr/bin/FreeCAD -t TestPartDesignGui
/TestSketcherGui :
test: xvfb-run /usr/bin/FreeCAD -t TestSketcherGui
/TestTechDrawGui :
test: xvfb-run /usr/bin/FreeCAD -t TestTechDrawGui
/Workbench :
+1 -30
View File
@@ -8,9 +8,7 @@ if [[ ${HOST} =~ .*darwin.* ]]; then
# add hacks for osx here!
echo "adding hacks for osx"
# Install 3DConnexion (SpaceMouse) driver
# Note: For local builds, comment out the following lines if you encounter issues
# installing the driver or don't need SpaceMouse support
# install space-mouse
/usr/bin/curl -o /tmp/3dFW.dmg -L 'https://download.3dconnexion.com/drivers/mac/10-7-0_B564CC6A-6E81-42b0-82EC-418EA823B81A/3DxWareMac_v10-7-0_r3411.dmg'
hdiutil attach -readonly /tmp/3dFW.dmg
sudo installer -package /Volumes/3Dconnexion\ Software/Install\ 3Dconnexion\ software.pkg -target /
@@ -19,31 +17,6 @@ if [[ ${HOST} =~ .*darwin.* ]]; then
CMAKE_PLATFORM_FLAGS+=(-D3DCONNEXIONCLIENT_FRAMEWORK:FILEPATH="/Library/Frameworks/3DconnexionClient.framework")
CXXFLAGS="${CXXFLAGS} -D_LIBCPP_DISABLE_AVAILABILITY"
# Use MACOS_DEPLOYMENT_TARGET from environment, default to 11.0 for backwards compat.
# Note that CI sets this per target: 10.13 (Intel), 11.0 (ARM legacy), 15.0 (ARM modern)
# - macOS 10.13+ Intel: legacy QuickLook generator (.qlgenerator)
# - macOS 11-14 ARM: legacy QuickLook generator (.qlgenerator)
# - macOS 15+ ARM: modern QuickLook App Extensions (.appex)
DEPLOY_TARGET="${MACOS_DEPLOYMENT_TARGET:-11.0}"
CMAKE_PLATFORM_FLAGS+=(-DCMAKE_OSX_DEPLOYMENT_TARGET=${DEPLOY_TARGET})
# Patch Qt6's FindWrapOpenGL.cmake to not link AGL on macOS 10.15+
# AGL framework was removed in macOS 10.15 Catalina and Qt6's cmake
# unconditionally tries to link it on Apple platforms, causing build failures.
# Only apply this patch for deployment targets >= 10.15.
# See: https://github.com/conda-forge/qt-main-feedstock/issues/240
DEPLOY_MAJOR=$(echo "$DEPLOY_TARGET" | cut -d. -f1)
DEPLOY_MINOR=$(echo "$DEPLOY_TARGET" | cut -d. -f2)
if [[ "$DEPLOY_MAJOR" -gt 10 ]] || [[ "$DEPLOY_MAJOR" -eq 10 && "$DEPLOY_MINOR" -ge 15 ]]; then
FIND_WRAP_OPENGL="$PREFIX/lib/cmake/Qt6/FindWrapOpenGL.cmake"
if [[ -f "$FIND_WRAP_OPENGL" ]]; then
echo "Patching Qt6 FindWrapOpenGL.cmake to remove AGL linkage (not available on macOS 10.15+)..."
sed -i.bak \
-e '/find_library(WrapOpenGL_AGL/,/target_link_libraries.*__opengl_agl_fw_path/d' \
"$FIND_WRAP_OPENGL"
fi
fi
fi
unset CMAKE_GENERATOR
@@ -51,7 +24,6 @@ unset CMAKE_GENERATOR_PLATFORM
cmake \
${CMAKE_ARGS} \
${CMAKE_PLATFORM_FLAGS[@]} \
--preset ${CMAKE_PRESET} \
-D CMAKE_IGNORE_PREFIX_PATH="/opt/homebrew;/usr/local/homebrew" \
-D CMAKE_INCLUDE_PATH:FILEPATH="$PREFIX/include" \
@@ -65,7 +37,6 @@ cmake \
-D OCC_LIBRARY_DIR:FILEPATH="$PREFIX/lib" \
-D Python_EXECUTABLE:FILEPATH="$PYTHON" \
-D Python3_EXECUTABLE:FILEPATH="$PYTHON" \
-D BUILD_DYNAMIC_LINK_PYTHON:BOOL=OFF \
-B build \
-S .
+1 -1
View File
@@ -31,4 +31,4 @@ else
MAIN="$HERE/usr/bin/freecad"
fi
exec "${MAIN}" "$@"
"${MAIN}" "$@"
+6 -33
View File
@@ -48,11 +48,7 @@ mkdir -p FreeCAD.app/Contents/MacOS
cp build/FreeCAD FreeCAD.app/Contents/MacOS/FreeCAD
python_version=$(${conda_env}/bin/python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
# Add deployment target suffix to artifact name (e.g., "-macOS11" or "-macOS15")
deploy_target="${MACOS_DEPLOYMENT_TARGET:-11.0}"
deploy_suffix="-macOS${deploy_target%%.*}"
version_name="FreeCAD_${BUILD_TAG}-macOS-$(uname -m)${deploy_suffix}-${python_version}"
version_name="FreeCAD_${BUILD_TAG}-macOS-$(uname -m)-${python_version}"
application_menu_name="FreeCAD_${BUILD_TAG}"
echo -e "\################"
@@ -66,37 +62,14 @@ sed -i "s/APPLICATION_MENU_NAME/${application_menu_name}/" ${conda_env}/../Info.
pixi list -e default > FreeCAD.app/Contents/packages.txt
sed -i '1s/.*/\nLIST OF PACKAGES:/' FreeCAD.app/Contents/packages.txt
# move plugins into their final location (Library only exists for macOS < 15.0 builds)
if [ -d "${conda_env}/Library" ]; then
mv ${conda_env}/Library ${conda_env}/..
fi
# copy the plugin into its final location
cp -a ${conda_env}/Library ${conda_env}/..
rm -rf ${conda_env}/Library
# move App Extensions (PlugIns) to the correct location for macOS registration
if [ -d "${conda_env}/PlugIns" ]; then
mv ${conda_env}/PlugIns ${conda_env}/..
fi
if [[ "${MACOS_SIGN_RELEASE}" == "true" ]]; then
if [[ "${SIGN_RELEASE}" == "true" ]]; then
# create the signed dmg
../../scripts/macos_sign_and_notarize.zsh -p "FreeCAD" -k ${MACOS_SIGNING_KEY_ID} -o "${version_name}.dmg"
../../scripts/macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
else
# Ad-hoc sign for local builds (required for QuickLook extensions to register)
if [ -d "FreeCAD.app/Contents/PlugIns" ]; then
echo "Ad-hoc signing App Extensions with entitlements..."
codesign --force --sign - \
--entitlements ../../../src/MacAppBundle/QuickLook/modern/ThumbnailExtension.entitlements \
FreeCAD.app/Contents/PlugIns/FreeCADThumbnailExtension.appex
codesign --force --sign - \
--entitlements ../../../src/MacAppBundle/QuickLook/modern/PreviewExtension.entitlements \
FreeCAD.app/Contents/PlugIns/FreeCADPreviewExtension.appex
fi
echo "Ad-hoc signing app bundle..."
codesign --force --sign - FreeCAD.app/Contents/packages.txt
if [ -f "FreeCAD.app/Contents/Library/QuickLook/QuicklookFCStd.qlgenerator/Contents/MacOS/QuicklookFCStd" ]; then
codesign --force --sign - FreeCAD.app/Contents/Library/QuickLook/QuicklookFCStd.qlgenerator/Contents/MacOS/QuicklookFCStd
fi
codesign --force --sign - FreeCAD.app
# create the dmg
dmgbuild -s dmg_settings.py "FreeCAD" "${version_name}.dmg"
fi
@@ -1,12 +1,7 @@
cmake_minimum_required(VERSION 3.20)
project(freecad-launcher LANGUAGES CXX)
# Use MACOS_DEPLOYMENT_TARGET from environment if set, otherwise keep original default
if(DEFINED ENV{MACOS_DEPLOYMENT_TARGET})
set(CMAKE_OSX_DEPLOYMENT_TARGET "$ENV{MACOS_DEPLOYMENT_TARGET}" CACHE STRING "Minimum OS X deployment version" FORCE)
else()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version" FORCE)
endif()
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.13" CACHE STRING "Minimum OS X deployment version" FORCE)
set(CMAKE_CXX_STANDARD 17)
add_executable(FreeCAD FreeCAD.cpp)
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -8,7 +8,7 @@ preview = ["pixi-build"]
[package]
name = "freecad"
version = "1.1.0dev"
version = "1.1.0rc1"
homepage = "https://freecad.org"
repository = "https://github.com/FreeCAD/FreeCAD"
description = "FreeCAD"
@@ -28,12 +28,10 @@ create_bundle = 'bash -c "cd $(bash scripts/get_os.bash) && bash create_bundle.s
## Linux (x86-64)
[feature.package.target.linux-64.dependencies]
coreutils = "*"
xcb-util-cursor = "==0.1.5" ## hack to address https://github.com/FreeCAD/FreeCAD/issues/26726
## Linux (aarch64)
[feature.package.target.linux-aarch64.dependencies]
coreutils = "*"
xcb-util-cursor = "==0.1.5" ## hack to address https://github.com/FreeCAD/FreeCAD/issues/26726
## macOS (Intel)
[feature.package.target.osx-64.dependencies]
+3 -8
View File
@@ -1,5 +1,5 @@
context:
version: "1.1.0dev"
version: "1.1.0rc1"
package:
name: freecad
@@ -22,7 +22,7 @@ requirements:
- noqt5
- python>=3.11,<3.12
- qt6-main>=6.8,<6.9
- swig>=4.3,<4.4
- swig
- if: linux and x86_64
then:
@@ -52,7 +52,6 @@ requirements:
- pixman-cos7-x86_64
- sed
- sysroot_linux-64
- xcb-util-cursor==0.1.5 ## hack to address https://github.com/FreeCAD/FreeCAD/issues/26726
- xorg-x11-server-common-cos7-x86_64
- xorg-x11-server-xvfb-cos7-x86_64
- xorg-xproto
@@ -85,7 +84,6 @@ requirements:
- pixman-cos7-aarch64
- sed
- sysroot_linux-aarch64
- xcb-util-cursor==0.1.5 ## hack to address https://github.com/FreeCAD/FreeCAD/issues/26726
- xorg-x11-server-common-cos7-aarch64
- xorg-x11-server-xvfb-cos7-aarch64
- xorg-xproto
@@ -100,11 +98,10 @@ requirements:
host:
- coin3d
- eigen >=3.3,<5
- eigen
- fmt
- freetype
- hdf5
- lark
- libboost-devel
- matplotlib-base
- noqt5
@@ -178,5 +175,3 @@ requirements:
- if: linux
then:
- libspnav
- qt6-wayland
- xcb-util-cursor==0.1.5 ## hack to address https://github.com/FreeCAD/FreeCAD/issues/26726
@@ -44,9 +44,6 @@ find ${copy_dir} -name \*arm\*.exe -delete # arm binaries that fail to extract u
mv ${copy_dir}/bin/Lib/ssl.py .ssl-orig.py
cp ssl-patch.py ${copy_dir}/bin/Lib/ssl.py
# Turn off the echo before we start actually calling "echo"
set +x
echo '[Paths]' >> ${copy_dir}/bin/qt6.conf
echo 'Prefix = ../lib/qt6' >> ${copy_dir}/bin/qt6.conf
@@ -70,64 +67,6 @@ sed -i '1s/.*/\nLIST OF PACKAGES:/' ${copy_dir}/packages.txt
mv ${copy_dir} ${version_name}
# Sign the EXE, DLL, and PYD files (if we can access the Azure account for signing):
set -euo pipefail
SIGN_DIR="${version_name}"
if [[ "${WINDOWS_SIGN_RELEASE:-0}" == "1" ]]; then
TENANT="$(az account show --query tenantId -o tsv)"
export AZURE_IDENTITY_DISABLE_WORKLOAD_IDENTITY=true
export AZURE_IDENTITY_DISABLE_MANAGED_IDENTITY=true
unset AZURE_IDENTITY_LOGGING_ENABLED
if az account get-access-token \
--tenant "$TENANT" \
--scope "https://codesigning.azure.net/.default" \
>/dev/null 2>&1;
then
echo "Azure Artifact Signing access confirmed. Beginning signing process..."
shopt -s nullglob
FILES=(
"$SIGN_DIR"/*.exe
"$SIGN_DIR"/bin/*.exe
"$SIGN_DIR"/bin/*.dll
"$SIGN_DIR"/bin/*.pyd
)
count=0
total=${#FILES[@]}
echo "Signing $total files"
for f in "${FILES[@]}"; do
((count+=1))
echo "Signing [$count/$total]: $f"
sign code artifact-signing \
--artifact-signing-endpoint "${WINDOWS_AZURE_ENDPOINT}" \
--artifact-signing-certificate-profile "${WINDOWS_AZURE_CERTIFICATE_PROFILE}" \
--artifact-signing-account "${WINDOWS_AZURE_SIGNING_ACCOUNT}" \
--timestamp-url https://timestamp.acs.microsoft.com \
--timestamp-digest sha256 \
"$f" >/dev/null 2>&1
# Output was redirected to /dev/null because Azure authentication is absurdly noisy, with constant misleading
# "failure" messages about Managed Identity authentication failing. We don't use, or want to use, that
# authentication, and the fact that it fails is not a problem as long as the real authentication succeeds.
done
# Manually check the important one!
signtool verify -pa "$SIGN_DIR/bin/FreeCAD.exe"
echo "Signing completed."
else
echo "Signing requested, but no Azure Artifact Signing available -- skipping signing."
fi
else
echo "Not logged into Azure -- skipping signing."
fi
7z a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
# create hash
sha256sum ${version_name}.7z > ${version_name}.7z-SHA256.txt
@@ -149,28 +88,6 @@ if [ "${MAKE_INSTALLER}" == "true" ]; then
-X'SetCompressor /FINAL lzma' \
../../WindowsInstaller/FreeCAD-installer.nsi
mv ../../WindowsInstaller/${version_name}-installer.exe .
echo "Created installer ${version_name}-installer.exe"
# See if we can sign the installer exe as well:
if [[ "${WINDOWS_SIGN_RELEASE:-0}" == "1" ]] && \
az account get-access-token \
--tenant "$TENANT" \
--scope "https://codesigning.azure.net/.default" \
>/dev/null 2>&1;
then
echo "Signing the installer..."
sign code artifact-signing \
--artifact-signing-endpoint "${WINDOWS_AZURE_ENDPOINT}" \
--artifact-signing-certificate-profile "${WINDOWS_AZURE_CERTIFICATE_PROFILE}" \
--artifact-signing-account "${WINDOWS_AZURE_SIGNING_ACCOUNT}" \
--timestamp-url https://timestamp.acs.microsoft.com \
--timestamp-digest sha256 \
${version_name}-installer.exe >/dev/null 2>&1 \
|| { echo "Signing the installer failed!"; exit 1; }
else
echo "No code signing available, leaving the installer unsigned"
fi
sha256sum ${version_name}-installer.exe > ${version_name}-installer.exe-SHA256.txt
else
echo "Error: Failed to get NsProcess plugin. Aborting installer creation..."
@@ -179,10 +96,8 @@ if [ "${MAKE_INSTALLER}" == "true" ]; then
fi
if [ "${UPLOAD_RELEASE}" == "true" ]; then
echo "Uploading the release..."
gh release upload --clobber ${BUILD_TAG} "${version_name}.7z" "${version_name}.7z-SHA256.txt"
if [ "${MAKE_INSTALLER}" == "true" ]; then
gh release upload --clobber ${BUILD_TAG} "${version_name}-installer.exe" "${version_name}-installer.exe-SHA256.txt"
fi
echo "Done uploading"
fi
+2 -42
View File
@@ -96,13 +96,6 @@ function run_codesign {
/usr/bin/codesign --options runtime -f -s ${SIGNING_KEY_ID} --timestamp --entitlements entitlements.plist "$1"
}
function run_codesign_extension {
local target="$1"
local entitlements_file="$2"
echo "Signing extension $target with entitlements $entitlements_file"
/usr/bin/codesign --options runtime -f -s ${SIGNING_KEY_ID} --timestamp --entitlements "$entitlements_file" "$target"
}
IFS=$'\n'
dylibs=($(/usr/bin/find "${CONTAINING_FOLDER}/${APP_NAME}" -name "*.dylib"))
shared_objects=($(/usr/bin/find "${CONTAINING_FOLDER}/${APP_NAME}" -name "*.so"))
@@ -115,45 +108,12 @@ signed_files=("${dylibs[@]}" "${shared_objects[@]}" "${bundles[@]}" "${executabl
# This list of files is generated from:
# file `find . -type f -perm +111 -print` | grep "Mach-O 64-bit executable" | sed 's/:.*//g'
for exe in ${signed_files}; do
# Skip .appex executables as they will be signed separately with their bundles
if [[ "$exe" != */Contents/PlugIns/*.appex/* ]]; then
run_codesign "${exe}"
fi
run_codesign "${exe}"
done
# Two additional files that must be signed that aren't caught by the above searches:
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}/Contents/packages.txt"
# Sign legacy QuickLook generator if present (not built for macOS 15.0+)
if [ -f "${CONTAINING_FOLDER}/${APP_NAME}/Contents/Library/QuickLook/QuicklookFCStd.qlgenerator/Contents/MacOS/QuicklookFCStd" ]; then
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}/Contents/Library/QuickLook/QuicklookFCStd.qlgenerator/Contents/MacOS/QuicklookFCStd"
fi
# Sign new Swift QuickLook extensions (macOS 15.0+) with their specific entitlements
# These must be signed before the app itself to avoid overriding the extension signatures
if [ -d "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns" ]; then
# Find the entitlements files relative to script location
# Script is in package/scripts/, entitlements are in src/MacAppBundle/QuickLook/modern/
SCRIPT_DIR="${0:A:h}" # zsh equivalent of dirname with full path resolution
PREVIEW_ENTITLEMENTS="${SCRIPT_DIR}/../../src/MacAppBundle/QuickLook/modern/PreviewExtension.entitlements"
THUMBNAIL_ENTITLEMENTS="${SCRIPT_DIR}/../../src/MacAppBundle/QuickLook/modern/ThumbnailExtension.entitlements"
# Sign individual executables within .appex bundles first
if [ -f "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADThumbnailExtension.appex/Contents/MacOS/FreeCADThumbnailExtension" ]; then
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADThumbnailExtension.appex/Contents/MacOS/FreeCADThumbnailExtension"
fi
if [ -f "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADPreviewExtension.appex/Contents/MacOS/FreeCADPreviewExtension" ]; then
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADPreviewExtension.appex/Contents/MacOS/FreeCADPreviewExtension"
fi
# Then sign the .appex bundles themselves with extension-specific entitlements
if [ -d "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADThumbnailExtension.appex" ] && [ -f "$THUMBNAIL_ENTITLEMENTS" ]; then
run_codesign_extension "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADThumbnailExtension.appex" "$THUMBNAIL_ENTITLEMENTS"
fi
if [ -d "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADPreviewExtension.appex" ] && [ -f "$PREVIEW_ENTITLEMENTS" ]; then
run_codesign_extension "${CONTAINING_FOLDER}/${APP_NAME}/Contents/PlugIns/FreeCADPreviewExtension.appex" "$PREVIEW_ENTITLEMENTS"
fi
fi
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}/Contents/Library/QuickLook/QuicklookFCStd.qlgenerator/Contents/MacOS/QuicklookFCStd"
# Finally, sign the app itself (must be done last)
run_codesign "${CONTAINING_FOLDER}/${APP_NAME}"
+22 -20
View File
@@ -1,14 +1,15 @@
#!/bin/bash
set -euo pipefail
# Add the KDE Neon repository for up-to-date and matching Qt6 and PySide packages
# Ubuntu 24.04 does not have PySide6 packages available
sudo wget -qO- http://archive.neon.kde.org/public.key | sudo gpg --dearmor -o /usr/share/keyrings/neon-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/neon-keyring.gpg] http://archive.neon.kde.org/user noble main" | sudo tee /etc/apt/sources.list.d/neon-qt.list
# Update package lists quietly
sudo apt-get update -qq
if apt-cache show libvtk9-dev >/dev/null 2>&1; then
vtk_dev="libvtk9-dev"
else
vtk_dev="libvtk7-dev"
fi
packages=(
ccache
cmake
@@ -34,11 +35,13 @@ packages=(
libopencv-dev
libproj-dev
libpcl-dev
libpyside6-dev
libqt6opengl6-dev
libqt6svg6-dev
libpyside2-dev
libqt5opengl5-dev
libqt5svg5-dev
libqt5x11extras5-dev
libshiboken2-dev
libspnav-dev
libvtk9-dev
${vtk_dev}
libx11-dev
libxerces-c-dev
libyaml-cpp-dev
@@ -47,26 +50,25 @@ packages=(
netgen-headers
ninja-build
occt-draw
pyside6-tools
pyqt5-dev-tools
pyside2-tools
python3-dev
python3-defusedxml
python3-git
python3-lark
python3-markdown
python3-matplotlib
python3-packaging
python3-pivy
python3-ply
python3-pybind11
python3-pyside6.qtcore
python3-pyside6.qtgui
python3-pyside6.qtnetwork
python3-pyside6.qtsvg
python3-pyside6.qtwidgets
qt6-base-dev
qt6-l10n-tools
qt6-tools-dev
qt6-tools-dev-tools
python3-pyside2.qtcore
python3-pyside2.qtgui
python3-pyside2.qtnetwork
python3-pyside2.qtsvg
python3-pyside2.qtwidgets
qtbase5-dev
qttools5-dev
shiboken2
swig
xvfb
)
+6723 -7359
View File
File diff suppressed because it is too large Load Diff
+1 -6
View File
@@ -19,12 +19,11 @@ debugpy = "*"
defusedxml = "*"
docutils = "*"
doxygen = "*"
eigen = ">=3.3,<5"
eigen = "*"
fmt = "*"
freetype = "*"
git = "*"
graphviz = "*"
harfbuzz = "*"
hdf5 = "*"
ifcopenshell = "*"
lark = "*"
@@ -93,10 +92,8 @@ mesa-libgl-cos7-x86_64 = "*"
mesa-libgl-devel-cos7-x86_64 = "*"
mold = "*"
pixman-cos7-x86_64 = "*"
qt6-wayland = ">=6.8,<6.9"
sed = "*"
sysroot_linux-64 = "*"
xcb-util-cursor = "==0.1.5" ## hack to address https://github.com/FreeCAD/FreeCAD/issues/26726
xorg-x11-server-common-cos7-x86_64 = "*"
xorg-x11-server-xvfb-cos7-x86_64 = "*"
xorg-xproto = "*"
@@ -133,10 +130,8 @@ mesa-libgl-devel-cos7-aarch64 = "*"
mesa-libglapi-cos7-aarch64 = "*"
mold = "*"
pixman-cos7-aarch64 = "*"
qt6-wayland = ">=6.8,<6.9"
sed = "*"
sysroot_linux-aarch64 = "*"
xcb-util-cursor = "==0.1.5" ## hack to address https://github.com/FreeCAD/FreeCAD/issues/26726
xorg-x11-server-common-cos7-aarch64 = "*"
xorg-x11-server-xvfb-cos7-aarch64 = "*"
xorg-xproto = "*"
-2
View File
@@ -1,5 +1,3 @@
add_subdirectory(FastSignals)
# Build SalomeMesh for all Platforms since heavily patched
if (BUILD_SMESH)
add_subdirectory(salomesmesh)
-102
View File
@@ -1,102 +0,0 @@
---
Language: Cpp
# BasedOnStyle: WebKit
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: true
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
BreakBeforeBinaryOperators: All
BreakBeforeBraces: Custom
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 0
CommentPragmas: '^ IWYU pragma:'
BreakBeforeInheritanceComma: true
FixNamespaceComments: true
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: false
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
# FixNamespaceComments: false
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
IncludeCategories:
- Regex: '^"(stdafx|PrecompiledHeader)'
Priority: -2
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 4
- Regex: '^(<|"(gtest|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 2
IncludeIsMainRegex: '$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: 'BEGIN_MESSAGE_MAP_CUSTOM$|BEGIN_MSG_MAP$|BEGIN_SINK_MAP$|BEGIN_MESSAGE_MAP$|BOOST_FIXTURE_TEST_SUITE$|BOOST_AUTO_TEST_SUITE$|BEGIN_DLGRESIZE_MAP$|BEGIN_MSG_MAP_EX$|BEGIN_DDX_MAP$|BEGIN_COM_MAP$|BEGIN_CONNECTION_POINT_MAP$|WTL_BEGIN_LAYOUT_MAP$|WTL_BEGIN_LAYOUT_CONTAINER$'
MacroBlockEnd: 'END_MESSAGE_MAP_CUSTOM$|END_MSG_MAP$|END_SINK_MAP$|END_MESSAGE_MAP$|BOOST_AUTO_TEST_SUITE_END$|END_DLGRESIZE_MAP$|END_DDX_MAP$|END_COM_MAP$|END_CONNECTION_POINT_MAP$|WTL_END_LAYOUT_MAP$|WTL_END_LAYOUT_CONTAINER$'
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 100
PenaltyReturnTypeOnItsOwnLine: 10000000
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 4
UseTab: Always
IndentPPDirectives: AfterHash
...
-367
View File
@@ -1,367 +0,0 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# ------------
# Custom Rules
build/
-12
View File
@@ -1,12 +0,0 @@
cmake_minimum_required(VERSION 3.10 FATAL_ERROR)
project(FastSignals)
include(cmake/functions.cmake)
add_subdirectory(libfastsignals)
if(BUILD_FASTSIGNALS_TESTING)
add_subdirectory(tests/libfastsignals_stress_tests)
add_subdirectory(tests/libfastsignals_unit_tests)
endif()
-21
View File
@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2018 iSpring Solutions Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-17
View File
@@ -1,17 +0,0 @@
# FastSignals
Yet another C++ signals and slots library
* Works as drop-in replacement for Boost.Signals2 with the same API
* Has better performance and more compact binary code
* Thread-safe in most operations, including concurrent connects/disconnects/emits
* Implemented with compact, pure C++17 code
[![Build Status](https://travis-ci.org/ispringteam/FastSignals.svg?branch=master)](https://travis-ci.org/ispringteam/FastSignals)
[![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
## Documentation
* [Why FastSignals?](docs/why-fastsignals.md)
* [Simple Examples](docs/simple-examples.md)
* [Migration from Boost.Signals2](docs/migration-from-boost-signals2.md)
-24
View File
@@ -1,24 +0,0 @@
# Function to add a library target.
function(custom_add_library_from_dir TARGET)
# Gather files from the current directory
file(GLOB TARGET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
add_library(${TARGET} ${TARGET_SRC})
endfunction()
# Function to add an executable target.
function(custom_add_executable_from_dir TARGET)
# Gather files from the current directory
file(GLOB TARGET_SRC "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
add_executable(${TARGET} ${TARGET_SRC})
endfunction()
# Function to add an executable target containing tests for a library.
function(custom_add_test_from_dir TARGET LIBRARY)
custom_add_executable_from_dir(${TARGET})
# Add path to Catch framework header
target_include_directories(${TARGET} PRIVATE "${CMAKE_SOURCE_DIR}/libs/catch")
# Link with the library being tested
target_link_libraries(${TARGET} ${LIBRARY})
# Register the executable with CMake as a test set
add_test(${TARGET} ${TARGET})
endfunction()
-94
View File
@@ -1,94 +0,0 @@
# Function bind_weak
## Usage
* Use `fastsignals::bind_weak` instead of `std::bind` to ensure that nothing happens if method called when binded object already destroyed
* Pass pointer to T class method as first argument, `shared_ptr<T>` or `weak_ptr<T>` as second argument
* Example: `bind_weak(&Document::save(), document, std::placeholders::_1)`, where `document` is a `weak_ptr<Document>` or `shared_ptr<Document>`
## Weak this idiom
The `fastsignals::bind_weak(...)` function implements "weak this" idiom. This idiom helps to avoid dangling pointers and memory access wiolations in asynchronous and/or multithreaded programs.
In the following example, we use weak this idiom to avoid using dangling pointer wehn calling `print()` method of the `Enityt`:
```cpp
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
std::function<void()> print_later()
{
// ! weak this idiom here !
auto weak_this = weak_from_this();
return [weak_this] {
if (auto shared_this = weak_this.lock())
{
shared_this->print();
}
};
}
};
int main()
{
auto entity = std::make_shared<Entity>();
auto print = entity->print_later();
// Prints OK.
print();
// Prints nothing - last shared_ptr to the Entity destroyed, so `weak_this.lock()` will return nullptr.
entity = nullptr;
print();
}
```
## Using bind_weak to avoid signal receiver lifetime issues
In the following example, `Entity::print()` method connected to the signal. Signal emmited once before and once after the `Entity` instance destroyed. However, no memory access violation happens: once `Entity` destoryed, no slot will be called because `bind_weak` doesn't call binded method if it cannot lock `std::weak_ptr` to binded object. The second `event()` expression just does nothing.
```cpp
#include <fastsignals/signal.h>
#include <fastsignals/bind_weak.h>
#include <iostream>
using VoidSignal = fastsignals::signal<void()>;
using VoidSlot = VoidSignal::slot_type;
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
VoidSlot get_print_slot()
{
// Here fastsignals::bind_weak() used instead of std::bind.
return fastsignals::bind_weak(&Entity::print, weak_from_this());
}
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
};
int main()
{
VoidSignal event;
auto entity = std::make_shared<Entity>();
event.connect(entity->get_print_slot());
// Here slot called - it prints `slot called, num = 42`
event();
entity = nullptr;
// Here nothing happens - no exception, no slot call.
event();
}
```
@@ -1,163 +0,0 @@
# Migration from Boost.Signals2
This guide helps to migrate large codebase from Boost.Signals2 to `FastSignals` signals/slots library. It helps to solve known migration issues in the right way.
During migrations, you will probably face with following things:
* You code uses `boost::signals2::` namespace and `<boost/signals2.hpp>` header directly
* You code uses third-party headers included implicitly by the `<boost/signals2.hpp>` header
## Reasons migrate from Boost.Signals2 to FastSignals
FastSignals API mostly compatible with Boost.Signals2 - there are differences, and all differences has their reasons explained below.
Comparing to Boost.Signals2, FastSignals has following pros:
* FastSignals is not header-only - so binary code will be more compact
* FastSignals implemented using C++17 with variadic templates, `constexpr if` and other modern metaprogramming techniques - so it compiles faster and, again, binary code will be more compact
* FastSignals probably will faster than Boost.Signals2 for your codebase because with FastSignals you don't pay for things that you don't use, with one exception: you always pay for the multithreading support
## Step 1: Create header with aliases
## Step 2: Rebuild and fix compile errors
### 2.1 Add missing includes
Boost.Signals2 is header-only library. It includes a lot of STL/Boost stuff while FastSignals does not:
```cpp
#include <boost/signals2.hpp>
// Also includes std::map, boost::variant, boost::optional, etc.
// Compiled OK even without `#include <map>`!
std::map CreateMyMap();
```
With FastSignals, you must include headers like `<map>` manually. The following table shows which files should be included explicitly if you see compile erros after migration.
| Class | Header |
|--------------------|:--------------------------------------:|
| std::map | `#include <map>` |
| boost::variant | `#include <boost/variant/variant.hpp>` |
| boost::optional | `#include <boost/optional/optional.hpp>` |
| boost::scoped_ptr | `#include <boost/scoped_ptr.hpp>` |
| boost::noncopyable | `#include <boost/noncopyable.hpp>` |
| boost::bind | `#include <boost/bind.hpp>` |
| boost::function | `#include <boost/function.hpp>` |
If you just want to compile you code, you can add following includes in you `signals.h` header:
```cpp
// WARNING: [libfastsignals] we do not recommend to include following extra headers.
#include <map>
#include <boost/variant/variant.hpp>
#include <boost/optional/optional.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>
```
### 2.2 Remove redundant returns for void signals
With Boost.Signals2, following code compiled without any warning:
```cpp
boost::signals2::signal<void()> event;
event.connect([] {
return true;
});
```
With FastSignals, slot cannot return non-void value when for `signal<void(...)>`. You must fix your code: just remove returns from your slots or add lambdas to wrap slot and ignore it result.
### 2.3 Replace track() and track_foreign() with bind_weak_ptr()
Boost.Signals2 [can track connected objects lifetype](https://www.boost.org/doc/libs/1_55_0/doc/html/signals2/tutorial.html#idp204830936) using `track(...)` and `track_foreign(...)` methods. In the following example `Entity` created with `make_shared`, and `Entity::get_print_slot()` creates slot function which tracks weak pointer to Entity:
```cpp
#include <boost/signals2.hpp>
#include <iostream>
#include <memory>
using VoidSignal = boost::signals2::signal<void()>;
using VoidSlot = VoidSignal::slot_type;
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
VoidSlot get_print_slot()
{
// Here track() tracks object itself.
return VoidSlot(std::bind(&Entity::print, this)).track_foreign(shared_from_this());
}
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
};
int main()
{
VoidSignal event;
auto entity = std::make_shared<Entity>();
event.connect(entity->get_print_slot());
// Here slot called - it prints `print called, num = 42`
event();
entity = nullptr;
// This call does nothing.
event();
}
```
FastSignals uses another approach: `bind_weak` function:
```cpp
#include <fastsignals/bind_weak.h>
#include <iostream>
using VoidSignal = fastsignals::signal<void()>;
using VoidSlot = VoidSignal::slot_type;
struct Entity : std::enable_shared_from_this<Entity>
{
int value = 42;
VoidSlot get_print_slot()
{
// Here fastsignals::bind_weak() used instead of std::bind.
return fastsignals::bind_weak(&Entity::print, weak_from_this());
}
void print()
{
std::cout << "print called, num = " << value << std::endl;
}
};
int main()
{
VoidSignal event;
auto entity = std::make_shared<Entity>();
event.connect(entity->get_print_slot());
// Here slot called - it prints `slot called, num = 42`
event();
entity = nullptr;
// Here nothing happens - no exception, no slot call.
event();
}
```
### FastSignals Differences in Result Combiners
## Step 3: Run Tests
Run all automated tests that you have (unit tests, integration tests, system tests, stress tests, benchmarks, UI tests).
Probably you will see no errors. If you see any, please report an issue.
-55
View File
@@ -1,55 +0,0 @@
# Simple Examples
>If you are not familar with Boost.Signals2, please read [Boost.Signals2: Connections](https://theboostcpplibraries.com/boost.signals2-connections)
## Example with signal&lt;&gt; and connection
```cpp
// Creates signal and connects 1 slot, calls 2 times, disconnects, calls again.
// Outputs:
// 13
// 17
#include "libfastsignals/signal.h"
using namespace fastsignals;
int main()
{
signal<void(int)> valueChanged;
connection conn;
conn = valueChanged.connect([](int value) {
cout << value << endl;
});
valueChanged(13);
valueChanged(17);
conn.disconnect();
valueChanged(42);
}
```
## Example with scoped_connection
```cpp
// Creates signal and connects 1 slot, calls 2 times, calls again after scoped_connection destroyed.
// - note: scoped_connection closes connection in destructor
// Outputs:
// 13
// 17
#include "libfastsignals/signal.h"
using namespace fastsignals;
int main()
{
signal<void(int)> valueChanged;
{
scoped_connection conn;
conn = valueChanged.connect([](int value) {
cout << value << endl;
});
valueChanged(13);
valueChanged(17);
}
valueChanged(42);
}
```
-40
View File
@@ -1,40 +0,0 @@
# Why FastSignals?
FastSignals is a C++17 signals/slots implementation which API is compatible with Boost.Signals2.
FastSignals pros:
* Faster than Boost.Signals2
* Has more compact binary code
* Has the same API as Boost.Signals2
FastSignals cons:
* Supports only C++17 compatible compilers: Visual Studio 2017, modern Clang, modern GCC
* Lacks a few rarely used features presented in Boost.Signals2
* No access to connection from slot with `signal::connect_extended` method
* No connected object tracking with `slot::track` method
* Use [bind_weak](bind_weak.md) instead
* No temporary signal blocking with `shared_connection_block` class
* Cannot disconnect equivalent slots since no `disconnect(slot)` function overload
* Any other API difference is a bug - please report it!
See also [Migration from Boost.Signals2](migration-from-boost-signals2.md).
## Benchmark results
Directory `tests/libfastsignals_bench` contains simple benchmark with compares two signal/slot implementations:
* Boost.Signals2
* libfastsignals
Benchmark compairs performance when signal emitted frequently with 0, 1 and 8 active connections. In these cases libfastsignals is 3-6 times faster.
```
*** Results:
measure emit_boost emit_fastsignals
emit_boost/0 1.00 3.00
emit_boost/1 1.00 5.76
emit_boost/8 1.00 3.70
***
```
@@ -1,5 +0,0 @@
file(GLOB LIBFASTSIGNALS_SRC "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h")
add_library(libfastsignals STATIC ${LIBFASTSIGNALS_SRC})
set_target_properties(libfastsignals PROPERTIES POSITION_INDEPENDENT_CODE ON)
target_include_directories(libfastsignals INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -1,79 +0,0 @@
#pragma once
#include <functional>
#include <memory>
#include <type_traits>
namespace fastsignals
{
namespace detail
{
template <class ReturnType, class ClassType, bool AddConst, class... Args>
struct weak_binder
{
using ConstMethodType = ReturnType (ClassType::*)(Args... args) const;
using NonConstMethodType = ReturnType (ClassType::*)(Args... args);
using MethodType = std::conditional_t<AddConst, ConstMethodType, NonConstMethodType>;
using WeakPtrType = std::weak_ptr<ClassType>;
weak_binder(MethodType pMethod, WeakPtrType&& pObject)
: m_pMethod(pMethod)
, m_pObject(std::move(pObject))
{
}
ReturnType operator()(Args... args) const
{
if (auto pThis = m_pObject.lock())
{
return (pThis.get()->*m_pMethod)(std::forward<Args>(args)...);
}
return ReturnType();
}
MethodType m_pMethod;
WeakPtrType m_pObject;
};
} // namespace detail
/// Weak this binding of non-const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args), std::shared_ptr<ClassType> const& pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, false, Params...>;
weak_binder_alias invoker(memberFn, std::weak_ptr<ClassType>(pThis));
return std::bind(invoker, args...);
}
/// Weak this binding of const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args) const, std::shared_ptr<ClassType> const& pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, true, Params...>;
weak_binder_alias invoker(memberFn, std::weak_ptr<ClassType>(pThis));
return std::bind(invoker, args...);
}
/// Weak this binding of non-const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args), std::weak_ptr<ClassType> pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, false, Params...>;
weak_binder_alias invoker(memberFn, std::move(pThis));
return std::bind(invoker, args...);
}
/// Weak this binding of const methods.
template <typename ReturnType, typename ClassType, typename... Params, typename... Args>
decltype(auto) bind_weak(ReturnType (ClassType::*memberFn)(Params... args) const, std::weak_ptr<ClassType> pThis, Args... args)
{
using weak_binder_alias = detail::weak_binder<ReturnType, ClassType, true, Params...>;
weak_binder_alias invoker(memberFn, std::move(pThis));
return std::bind(invoker, args...);
}
} // namespace fastsignals
@@ -1,40 +0,0 @@
#pragma once
#include <optional>
namespace fastsignals
{
/**
* This results combiner reduces results collection into last value of this collection.
* In other words, it keeps only result of the last slot call.
*/
template <class T>
class optional_last_value
{
public:
using result_type = std::optional<T>;
template <class TRef>
void operator()(TRef&& value)
{
m_result = std::forward<TRef>(value);
}
result_type get_value() const
{
return m_result;
}
private:
result_type m_result = {};
};
template <>
class optional_last_value<void>
{
public:
using result_type = void;
};
} // namespace fastsignals
@@ -1,116 +0,0 @@
#pragma once
#include "signal_impl.h"
namespace fastsignals
{
// Connection keeps link between signal and slot and can disconnect them.
// Disconnect operation is thread-safe: any thread can disconnect while
// slots called on other thread.
// This class itself is not thread-safe: you can't use the same connection
// object from different threads at the same time.
class connection
{
public:
connection() noexcept;
explicit connection(detail::signal_impl_weak_ptr storage, uint64_t id) noexcept;
connection(const connection& other) noexcept;
connection& operator=(const connection& other) noexcept;
connection(connection&& other) noexcept;
connection& operator=(connection&& other) noexcept;
~connection() = default;
bool connected() const noexcept;
void disconnect() noexcept;
protected:
detail::signal_impl_weak_ptr m_storage;
uint64_t m_id = 0;
};
// Connection class that supports blocking callback execution
class advanced_connection : public connection
{
public:
struct advanced_connection_impl
{
void block() noexcept;
void unblock() noexcept;
bool is_blocked() const noexcept;
private:
std::atomic<int> m_blockCounter {0};
};
using impl_ptr = std::shared_ptr<advanced_connection_impl>;
advanced_connection() noexcept;
explicit advanced_connection(connection&& conn, impl_ptr&& impl) noexcept;
advanced_connection(const advanced_connection&) noexcept;
advanced_connection& operator=(const advanced_connection&) noexcept;
advanced_connection(advanced_connection&& other) noexcept;
advanced_connection& operator=(advanced_connection&& other) noexcept;
~advanced_connection() = default;
protected:
impl_ptr m_impl;
};
// Blocks advanced connection, so its callback will not be executed
class shared_connection_block
{
public:
shared_connection_block(const advanced_connection& connection = advanced_connection(), bool initially_blocked = true) noexcept;
shared_connection_block(const shared_connection_block& other) noexcept;
shared_connection_block(shared_connection_block&& other) noexcept;
shared_connection_block& operator=(const shared_connection_block& other) noexcept;
shared_connection_block& operator=(shared_connection_block&& other) noexcept;
~shared_connection_block();
void block() noexcept;
void unblock() noexcept;
bool blocking() const noexcept;
private:
void increment_if_blocked() const noexcept;
std::weak_ptr<advanced_connection::advanced_connection_impl> m_connection;
std::atomic<bool> m_blocked {false};
};
// Scoped connection keeps link between signal and slot and disconnects them in destructor.
// Scoped connection is movable, but not copyable.
class scoped_connection : public connection
{
public:
scoped_connection() noexcept;
scoped_connection(const connection& conn) noexcept;
scoped_connection(connection&& conn) noexcept;
scoped_connection(const advanced_connection& conn) = delete;
scoped_connection(advanced_connection&& conn) noexcept = delete;
scoped_connection(const scoped_connection&) = delete;
scoped_connection& operator=(const scoped_connection&) = delete;
scoped_connection(scoped_connection&& other) noexcept;
scoped_connection& operator=(scoped_connection&& other) noexcept;
~scoped_connection();
connection release() noexcept;
};
// scoped connection for advanced connections
class advanced_scoped_connection : public advanced_connection
{
public:
advanced_scoped_connection() noexcept;
advanced_scoped_connection(const advanced_connection& conn) noexcept;
advanced_scoped_connection(advanced_connection&& conn) noexcept;
advanced_scoped_connection(const advanced_scoped_connection&) = delete;
advanced_scoped_connection& operator=(const advanced_scoped_connection&) = delete;
advanced_scoped_connection(advanced_scoped_connection&& other) noexcept;
advanced_scoped_connection& operator=(advanced_scoped_connection&& other) noexcept;
~advanced_scoped_connection();
advanced_connection release() noexcept;
};
} // namespace fastsignals
@@ -1,54 +0,0 @@
#pragma once
#include "function_detail.h"
namespace fastsignals
{
// Derive your class from not_directly_callable to prevent function from wrapping it using its template constructor
// Useful if your class provides custom operator for casting to function
struct not_directly_callable
{
};
template <class Fn, class Function, class Return, class... Arguments>
using enable_if_callable_t = typename std::enable_if_t<
!std::is_same_v<std::decay_t<Fn>, Function> && !std::is_base_of_v<not_directly_callable, std::decay_t<Fn>> && std::is_same_v<std::invoke_result_t<Fn, Arguments...>, Return>>;
template <class Signature>
class function;
// Compact function class - causes minimal code bloat when compiled.
// Replaces std::function in this library.
template <class Return, class... Arguments>
class function<Return(Arguments...)>
{
public:
function() = default;
function(const function& other) = default;
function(function&& other) noexcept = default;
function& operator=(const function& other) = default;
function& operator=(function&& other) noexcept = default;
template <class Fn, typename = enable_if_callable_t<Fn, function<Return(Arguments...)>, Return, Arguments...>>
function(Fn&& function) noexcept(detail::is_noexcept_packed_function_init<Fn, Return, Arguments...>)
{
m_packed.init<Fn, Return, Arguments...>(std::forward<Fn>(function));
}
Return operator()(Arguments&&... args) const
{
auto& proxy = m_packed.get<Return(Arguments...)>();
return proxy(std::forward<Arguments>(args)...);
}
detail::packed_function release() noexcept
{
return std::move(m_packed);
}
private:
detail::packed_function m_packed;
};
} // namespace fastsignals
@@ -1,164 +0,0 @@
#pragma once
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
namespace fastsignals::detail
{
/// Buffer for callable object in-place construction,
/// helps to implement Small Buffer Optimization.
static constexpr size_t inplace_buffer_size = (sizeof(int) == sizeof(void*) ? 8 : 6) * sizeof(void*);
/// Structure that has size enough to keep type "T" including vtable.
template <class T>
struct type_container
{
T data;
};
/// Type that has enough space to keep type "T" (including vtable) with suitable alignment.
using function_buffer_t = std::aligned_storage_t<inplace_buffer_size>;
/// Constantly is true if callable fits function buffer, false otherwise.
template <class T>
inline constexpr bool fits_inplace_buffer = (sizeof(type_container<T>) <= inplace_buffer_size);
// clang-format off
/// Constantly is true if callable fits function buffer and can be safely moved, false otherwise
template <class T>
inline constexpr bool can_use_inplace_buffer =
fits_inplace_buffer<T> &&
std::is_nothrow_move_constructible_v<T>;
// clang format on
/// Type that is suitable to keep copy of callable object.
/// - equal to pointer-to-function if Callable is pointer-to-function
/// - otherwise removes const/volatile and references to allow copying callable.
template <class Callable>
using callable_copy_t = std::conditional_t<std::is_function_v<std::remove_reference_t<Callable>>,
Callable,
std::remove_cv_t<std::remove_reference_t<Callable>>>;
class base_function_proxy
{
public:
virtual ~base_function_proxy() = default;
virtual base_function_proxy* clone(void* buffer) const = 0;
virtual base_function_proxy* move(void* buffer) noexcept = 0;
};
template <class Signature>
class function_proxy;
template <class Return, class... Arguments>
class function_proxy<Return(Arguments...)> : public base_function_proxy
{
public:
virtual Return operator()(Arguments&&...) = 0;
};
template <class Callable, class Return, class... Arguments>
class function_proxy_impl final : public function_proxy<Return(Arguments...)>
{
public:
// If you see this error, probably your function returns value and you're trying to
// connect it to `signal<void(...)>`. Just remove return value from callback.
static_assert(std::is_same_v<std::invoke_result_t<Callable, Arguments...>, Return>,
"cannot construct function<> class from callable object with different return type");
template <class FunctionObject>
explicit function_proxy_impl(FunctionObject&& function)
: m_callable(std::forward<FunctionObject>(function))
{
}
Return operator()(Arguments&&... args) final
{
return m_callable(std::forward<Arguments>(args)...);
}
base_function_proxy* clone(void* buffer) const final
{
if constexpr (can_use_inplace_buffer<function_proxy_impl>)
{
return new (buffer) function_proxy_impl(*this);
}
else
{
(void)buffer;
return new function_proxy_impl(*this);
}
}
base_function_proxy* move(void* buffer) noexcept final
{
if constexpr (can_use_inplace_buffer<function_proxy_impl>)
{
base_function_proxy* moved = new (buffer) function_proxy_impl(std::move(*this));
this->~function_proxy_impl();
return moved;
}
else
{
(void)buffer;
return this;
}
}
private:
callable_copy_t<Callable> m_callable;
};
template <class Fn, class Return, class... Arguments>
inline constexpr bool is_noexcept_packed_function_init = can_use_inplace_buffer<function_proxy_impl<Fn, Return, Arguments...>>;
class packed_function final
{
public:
packed_function() = default;
packed_function(packed_function&& other) noexcept;
packed_function(const packed_function& other);
packed_function& operator=(packed_function&& other) noexcept;
packed_function& operator=(const packed_function& other);
~packed_function() noexcept;
// Initializes packed function.
// Cannot be called without reset().
template <class Callable, class Return, class... Arguments>
void init(Callable&& function) noexcept(is_noexcept_packed_function_init<Callable, Return, Arguments...>)
{
using proxy_t = function_proxy_impl<Callable, Return, Arguments...>;
assert(m_proxy == nullptr);
if constexpr (can_use_inplace_buffer<proxy_t>)
{
m_proxy = new (&m_buffer) proxy_t{ std::forward<Callable>(function) };
}
else
{
m_proxy = new proxy_t{ std::forward<Callable>(function) };
}
}
template <class Signature>
function_proxy<Signature>& get() const
{
return static_cast<function_proxy<Signature>&>(unwrap());
}
void reset() noexcept;
private:
base_function_proxy* move_proxy_from(packed_function&& other) noexcept;
base_function_proxy* clone_proxy_from(const packed_function &other);
base_function_proxy& unwrap() const;
bool is_buffer_allocated() const noexcept;
function_buffer_t m_buffer[1] = {};
base_function_proxy* m_proxy = nullptr;
};
} // namespace fastsignals::detail
@@ -1,151 +0,0 @@
#pragma once
#include "combiners.h"
#include "connection.h"
#include "function.h"
#include "signal_impl.h"
#include "type_traits.h"
#include <type_traits>
namespace fastsignals
{
template <class Signature, template <class T> class Combiner = optional_last_value>
class signal;
struct advanced_tag
{
};
/// Signal allows to fire events to many subscribers (slots).
/// In other words, it implements one-to-many relation between event and listeners.
/// Signal implements observable object from Observable pattern.
template <class Return, class... Arguments, template <class T> class Combiner>
class signal<Return(Arguments...), Combiner> : private not_directly_callable
{
public:
using signature_type = Return(signal_arg_t<Arguments>...);
using slot_type = function<signature_type>;
using combiner_type = Combiner<Return>;
using result_type = typename combiner_type::result_type;
signal()
: m_slots(std::make_shared<detail::signal_impl>())
{
}
/// No copy construction
signal(const signal&) = delete;
/// Moves signal from other. Any operations on other except destruction, move, and swap are invalid
signal(signal&& other) = default;
/// No copy assignment
signal& operator=(const signal&) = delete;
/// Moves signal from other. Any operations on other except destruction, move, and swap are invalid
signal& operator=(signal&& other) = default;
/**
* connect(slot) method subscribes slot to signal emission event.
* Each time you call signal as functor, all slots are also called with given arguments.
* @returns connection - object which manages signal-slot connection lifetime
*/
connection connect(slot_type slot)
{
const uint64_t id = m_slots->add(slot.release());
return connection(m_slots, id);
}
/**
* connect(slot, advanced_tag) method subscribes slot to signal emission event with the ability to temporarily block slot execution
* Each time you call signal as functor, all non-blocked slots are also called with given arguments.
* You can temporarily block slot execution using shared_connection_block
* @returns advanced_connection - object which manages signal-slot connection lifetime
*/
advanced_connection connect(slot_type slot, advanced_tag)
{
static_assert(std::is_void_v<Return>, "Advanced connect can only be used with slots returning void (implementation limitation)");
auto conn_impl = std::make_shared<advanced_connection::advanced_connection_impl>();
slot_type slot_impl = [this, slot, weak_conn_impl = std::weak_ptr(conn_impl)](signal_arg_t<Arguments>... args) {
(void)this;
auto conn_impl = weak_conn_impl.lock();
if (!conn_impl || !conn_impl->is_blocked())
{
slot(args...);
}
};
auto conn = connect(std::move(slot_impl));
return advanced_connection(std::move(conn), std::move(conn_impl));
}
/**
* disconnect_all_slots() method disconnects all slots from signal emission event.
*/
void disconnect_all_slots() noexcept
{
m_slots->remove_all();
}
/**
* num_slots() method returns number of slots attached to this singal
*/
[[nodiscard]] std::size_t num_slots() const noexcept
{
return m_slots->count();
}
/**
* empty() method returns true if signal has any slots attached
*/
[[nodiscard]] bool empty() const noexcept
{
return m_slots->count() == 0;
}
/**
* operator(args...) calls all slots connected to this signal.
* Logically, it fires signal emission event.
*/
result_type operator()(signal_arg_t<Arguments>... args) const
{
return detail::signal_impl_ptr(m_slots)->invoke<combiner_type, result_type, signature_type, signal_arg_t<Arguments>...>(args...);
}
void swap(signal& other) noexcept
{
m_slots.swap(other.m_slots);
}
/**
* Allows using signals as slots for another signal
*/
operator slot_type() const noexcept
{
return [weakSlots = detail::signal_impl_weak_ptr(m_slots)](signal_arg_t<Arguments>... args) {
if (auto slots = weakSlots.lock())
{
return slots->invoke<combiner_type, result_type, signature_type, signal_arg_t<Arguments>...>(args...);
}
};
}
private:
detail::signal_impl_ptr m_slots;
};
} // namespace fastsignals
namespace std
{
// free swap function, findable by ADL
template <class Signature, template <class T> class Combiner>
void swap(
::fastsignals::signal<Signature, Combiner>& sig1,
::fastsignals::signal<Signature, Combiner>& sig2)
{
sig1.swap(sig2);
}
} // namespace std
@@ -1,59 +0,0 @@
#pragma once
#include "function_detail.h"
#include "spin_mutex.h"
#include <memory>
#include <vector>
namespace fastsignals::detail
{
class signal_impl
{
public:
uint64_t add(packed_function fn);
void remove(uint64_t id) noexcept;
void remove_all() noexcept;
size_t count() const noexcept;
template <class Combiner, class Result, class Signature, class... Args>
Result invoke(Args... args) const
{
packed_function slot;
size_t slotIndex = 0;
uint64_t slotId = 1;
if constexpr (std::is_same_v<Result, void>)
{
while (get_next_slot(slot, slotIndex, slotId))
{
slot.get<Signature>()(std::forward<Args>(args)...);
}
}
else
{
Combiner combiner;
while (get_next_slot(slot, slotIndex, slotId))
{
combiner(slot.get<Signature>()(std::forward<Args>(args)...));
}
return combiner.get_value();
}
}
private:
bool get_next_slot(packed_function& slot, size_t& expectedIndex, uint64_t& nextId) const;
mutable spin_mutex m_mutex;
std::vector<packed_function> m_functions;
std::vector<uint64_t> m_ids;
uint64_t m_nextId = 1;
};
using signal_impl_ptr = std::shared_ptr<signal_impl>;
using signal_impl_weak_ptr = std::weak_ptr<signal_impl>;
} // namespace fastsignals::detail
@@ -1,38 +0,0 @@
#pragma once
#include <atomic>
namespace fastsignals::detail
{
class spin_mutex
{
public:
spin_mutex() = default;
spin_mutex(const spin_mutex&) = delete;
spin_mutex& operator=(const spin_mutex&) = delete;
spin_mutex(spin_mutex&&) = delete;
spin_mutex& operator=(spin_mutex&&) = delete;
inline bool try_lock() noexcept
{
return !m_busy.test_and_set(std::memory_order_acquire);
}
inline void lock() noexcept
{
while (!try_lock())
{
/* do nothing */;
}
}
inline void unlock() noexcept
{
m_busy.clear(std::memory_order_release);
}
private:
std::atomic_flag m_busy = ATOMIC_FLAG_INIT;
};
} // namespace fastsignals::detail
@@ -1,24 +0,0 @@
#pragma once
namespace fastsignals
{
namespace detail
{
template <typename T>
struct signal_arg
{
using type = const T&;
};
template <typename U>
struct signal_arg<U&>
{
using type = U&;
};
} // namespace detail
template <typename T>
using signal_arg_t = typename detail::signal_arg<T>::type;
} // namespace fastsignals
@@ -1,251 +0,0 @@
#include "../include/fastsignals/connection.h"
namespace fastsignals
{
namespace
{
auto get_advanced_connection_impl(const advanced_connection& connection) noexcept
{
struct advanced_connection_impl_getter : private advanced_connection
{
advanced_connection_impl_getter(const advanced_connection& connection) noexcept
: advanced_connection(connection)
{
}
using advanced_connection::m_impl;
};
return advanced_connection_impl_getter(connection).m_impl;
}
} // namespace
connection::connection(connection&& other) noexcept
: m_storage(other.m_storage)
, m_id(other.m_id)
{
other.m_storage.reset();
other.m_id = 0;
}
connection::connection(detail::signal_impl_weak_ptr storage, uint64_t id) noexcept
: m_storage(std::move(storage))
, m_id(id)
{
}
connection::connection() noexcept = default;
connection::connection(const connection& other) noexcept = default;
connection& connection::operator=(connection&& other) noexcept
{
m_storage = other.m_storage;
m_id = other.m_id;
other.m_storage.reset();
other.m_id = 0;
return *this;
}
connection& connection::operator=(const connection& other) noexcept = default;
bool connection::connected() const noexcept
{
return (m_id != 0);
}
void connection::disconnect() noexcept
{
if (auto storage = m_storage.lock())
{
storage->remove(m_id);
m_storage.reset();
}
m_id = 0;
}
scoped_connection::scoped_connection(connection&& conn) noexcept
: connection(std::move(conn))
{
}
scoped_connection::scoped_connection(const connection& conn) noexcept
: connection(conn)
{
}
scoped_connection::scoped_connection() noexcept = default;
scoped_connection::scoped_connection(scoped_connection&& other) noexcept = default;
scoped_connection& scoped_connection::operator=(scoped_connection&& other) noexcept
{
disconnect();
static_cast<connection&>(*this) = std::move(other);
return *this;
}
scoped_connection::~scoped_connection()
{
disconnect();
}
connection scoped_connection::release() noexcept
{
connection conn = std::move(static_cast<connection&>(*this));
return conn;
}
bool advanced_connection::advanced_connection_impl::is_blocked() const noexcept
{
return m_blockCounter.load(std::memory_order_acquire) != 0;
}
void advanced_connection::advanced_connection_impl::block() noexcept
{
++m_blockCounter;
}
void advanced_connection::advanced_connection_impl::unblock() noexcept
{
--m_blockCounter;
}
advanced_connection::advanced_connection() noexcept = default;
advanced_connection::advanced_connection(connection&& conn, impl_ptr&& impl) noexcept
: connection(std::move(conn))
, m_impl(std::move(impl))
{
}
advanced_connection::advanced_connection(const advanced_connection&) noexcept = default;
advanced_connection::advanced_connection(advanced_connection&& other) noexcept = default;
advanced_connection& advanced_connection::operator=(const advanced_connection&) noexcept = default;
advanced_connection& advanced_connection::operator=(advanced_connection&& other) noexcept = default;
shared_connection_block::shared_connection_block(const advanced_connection& connection, bool initially_blocked) noexcept
: m_connection(get_advanced_connection_impl(connection))
{
if (initially_blocked)
{
block();
}
}
shared_connection_block::shared_connection_block(const shared_connection_block& other) noexcept
: m_connection(other.m_connection)
, m_blocked(other.m_blocked.load(std::memory_order_acquire))
{
increment_if_blocked();
}
shared_connection_block::shared_connection_block(shared_connection_block&& other) noexcept
: m_connection(other.m_connection)
, m_blocked(other.m_blocked.load(std::memory_order_acquire))
{
other.m_connection.reset();
other.m_blocked.store(false, std::memory_order_release);
}
shared_connection_block& shared_connection_block::operator=(const shared_connection_block& other) noexcept
{
if (&other != this)
{
unblock();
m_connection = other.m_connection;
m_blocked = other.m_blocked.load(std::memory_order_acquire);
increment_if_blocked();
}
return *this;
}
shared_connection_block& shared_connection_block::operator=(shared_connection_block&& other) noexcept
{
if (&other != this)
{
unblock();
m_connection = other.m_connection;
m_blocked = other.m_blocked.load(std::memory_order_acquire);
other.m_connection.reset();
other.m_blocked.store(false, std::memory_order_release);
}
return *this;
}
shared_connection_block::~shared_connection_block()
{
unblock();
}
void shared_connection_block::block() noexcept
{
bool blocked = false;
if (m_blocked.compare_exchange_strong(blocked, true, std::memory_order_acq_rel, std::memory_order_relaxed))
{
if (auto connection = m_connection.lock())
{
connection->block();
}
}
}
void shared_connection_block::unblock() noexcept
{
bool blocked = true;
if (m_blocked.compare_exchange_strong(blocked, false, std::memory_order_acq_rel, std::memory_order_relaxed))
{
if (auto connection = m_connection.lock())
{
connection->unblock();
}
}
}
bool shared_connection_block::blocking() const noexcept
{
return m_blocked;
}
void shared_connection_block::increment_if_blocked() const noexcept
{
if (m_blocked)
{
if (auto connection = m_connection.lock())
{
connection->block();
}
}
}
advanced_scoped_connection::advanced_scoped_connection() noexcept = default;
advanced_scoped_connection::advanced_scoped_connection(const advanced_connection& conn) noexcept
: advanced_connection(conn)
{
}
advanced_scoped_connection::advanced_scoped_connection(advanced_connection&& conn) noexcept
: advanced_connection(std::move(conn))
{
}
advanced_scoped_connection::advanced_scoped_connection(advanced_scoped_connection&& other) noexcept = default;
advanced_scoped_connection& advanced_scoped_connection::operator=(advanced_scoped_connection&& other) noexcept = default;
advanced_scoped_connection::~advanced_scoped_connection()
{
disconnect();
}
advanced_connection advanced_scoped_connection::release() noexcept
{
advanced_connection conn = std::move(static_cast<advanced_connection&>(*this));
return conn;
}
} // namespace fastsignals
@@ -1,96 +0,0 @@
#include "../include/fastsignals/function_detail.h"
#include <cstddef>
#include <functional>
namespace fastsignals::detail
{
packed_function::packed_function(packed_function&& other) noexcept
: m_proxy(move_proxy_from(std::move(other)))
{
}
packed_function::packed_function(const packed_function& other)
: m_proxy(clone_proxy_from(other))
{
}
packed_function& packed_function::operator=(packed_function&& other) noexcept
{
assert(this != &other);
reset();
m_proxy = move_proxy_from(std::move(other));
return *this;
}
base_function_proxy* packed_function::move_proxy_from(packed_function&& other) noexcept
{
auto proxy = other.m_proxy ? other.m_proxy->move(&m_buffer) : nullptr;
other.m_proxy = nullptr;
return proxy;
}
base_function_proxy* packed_function::clone_proxy_from(const packed_function& other)
{
return other.m_proxy ? other.m_proxy->clone(&m_buffer) : nullptr;
}
packed_function& packed_function::operator=(const packed_function& other)
{
if (this != &other)
{
if (other.is_buffer_allocated() && is_buffer_allocated())
{
// "This" and "other" are using SBO. Safe assignment must use copy+move
*this = packed_function(other);
}
else
{
// Buffer is used either by "this" or by "other" or not used at all.
// If this uses buffer then other's proxy is null or allocated on heap, so clone won't overwrite buffer
// If this uses heap or null then other's proxy can safely use buffer because reset() won't access buffer
auto newProxy = clone_proxy_from(other);
reset();
m_proxy = newProxy;
}
}
return *this;
}
packed_function::~packed_function() noexcept
{
reset();
}
void packed_function::reset() noexcept
{
if (m_proxy != nullptr)
{
if (is_buffer_allocated())
{
m_proxy->~base_function_proxy();
}
else
{
delete m_proxy;
}
m_proxy = nullptr;
}
}
base_function_proxy& packed_function::unwrap() const
{
if (m_proxy == nullptr)
{
throw std::bad_function_call();
}
return *m_proxy;
}
bool packed_function::is_buffer_allocated() const noexcept
{
return std::less_equal<const void*>()(&m_buffer[0], m_proxy)
&& std::less<const void*>()(m_proxy, &m_buffer[1]);
}
} // namespace fastsignals::detail
@@ -1,84 +0,0 @@
#include "../include/fastsignals/signal_impl.h"
#include <algorithm>
#include <mutex>
namespace fastsignals::detail
{
uint64_t signal_impl::add(packed_function fn)
{
std::lock_guard lock(m_mutex);
m_functions.emplace_back(std::move(fn));
try
{
m_ids.emplace_back(m_nextId);
}
catch (const std::bad_alloc& /*e*/)
{
// Remove function since we failed to add its id
m_functions.pop_back();
throw;
}
return m_nextId++;
}
void signal_impl::remove(uint64_t id) noexcept
{
std::lock_guard lock(m_mutex);
// We use binary search because ids array is always sorted.
auto it = std::lower_bound(m_ids.begin(), m_ids.end(), id);
if (it != m_ids.end() && *it == id)
{
size_t i = std::distance(m_ids.begin(), it);
m_ids.erase(m_ids.begin() + i);
m_functions.erase(m_functions.begin() + i);
}
}
void signal_impl::remove_all() noexcept
{
std::lock_guard lock(m_mutex);
m_functions.clear();
m_ids.clear();
}
bool signal_impl::get_next_slot(packed_function& slot, size_t& expectedIndex, uint64_t& nextId) const
{
// Slots always arranged by ID, so we can use a simple algorithm which avoids races:
// - on each step find first slot with ID >= slotId
// - after each call increment slotId
std::lock_guard lock(m_mutex);
// Avoid binary search if next slot wasn't moved between mutex locks.
if (expectedIndex >= m_ids.size() || m_ids[expectedIndex] != nextId)
{
auto it = (nextId < m_nextId)
? std::lower_bound(m_ids.cbegin(), m_ids.cend(), nextId)
: m_ids.end();
if (it == m_ids.end())
{
return false;
}
expectedIndex = std::distance(m_ids.cbegin(), it);
}
slot.reset();
slot = m_functions[expectedIndex];
nextId = (expectedIndex + 1 < m_ids.size()) ? m_ids[expectedIndex + 1] : m_ids[expectedIndex] + 1;
++expectedIndex;
return true;
}
size_t signal_impl::count() const noexcept
{
std::lock_guard lock(m_mutex);
return m_functions.size();
}
} // namespace fastsignals::detail
File diff suppressed because it is too large Load Diff
@@ -1,3 +0,0 @@
custom_add_test_from_dir(libfastsignals_stress_tests libfastsignals)
target_include_directories(libfastsignals_stress_tests PRIVATE "${CMAKE_SOURCE_DIR}/tests")
@@ -1,2 +0,0 @@
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
@@ -1,124 +0,0 @@
#include "catch2/catch.hpp"
#include <fastsignals/signal.h>
#include <array>
#include <mutex>
#include <random>
#include <vector>
#include <thread>
using namespace fastsignals;
namespace
{
using string_signal = signal<void(std::string)>;
using string_slot = string_signal::slot_type;
using void_signal = signal<void()>;
using void_slot = void_signal::slot_type;
class named_entity
{
public:
std::string name() const
{
std::lock_guard lock(m_nameMutex);
return m_name;
}
void fire_changed(std::string value)
{
bool fire = false;
{
std::lock_guard lock(m_nameMutex);
if (m_name != value)
{
m_name = std::move(value);
fire = true;
}
}
if (fire)
{
m_nameChanged(value);
}
}
connection on_name_changed(string_slot slot)
{
return m_nameChanged.connect(std::move(slot));
}
private:
mutable std::mutex m_nameMutex;
std::string m_name;
signal<void(std::string)> m_nameChanged;
};
unsigned get_next_seed()
{
static std::minstd_rand seedEngine(777);
return seedEngine();
}
size_t get_random_index(size_t size)
{
thread_local std::minstd_rand disconnectRandomEngine{ get_next_seed() };
std::uniform_int_distribution<size_t> disconnectIndexDistribution{ 0, size - 1 };
return disconnectIndexDistribution(disconnectRandomEngine);
}
} // namespace
TEST_CASE("Can work in a few threads", "[signal]")
{
constexpr unsigned fireThreadCount = 8;
constexpr unsigned signalsCount = 7;
constexpr unsigned fireCountPerThread = 100'000;
constexpr unsigned connectCallsCount = 80'000;
constexpr unsigned totalRunCount = 10;
for (unsigned i = 0; i < totalRunCount; ++i)
{
std::array<void_signal, signalsCount> signals;
std::mutex connectionsMutex;
std::vector<connection> connections;
connections.reserve(connectCallsCount);
std::vector<std::thread> threads;
auto slot = [&] {
std::lock_guard lock(connectionsMutex);
if (!connections.empty())
{
const size_t index = get_random_index(connections.size());
connections.at(index).disconnect();
}
};
threads.emplace_back([&] {
for (unsigned cci = 0; cci < connectCallsCount; ++cci)
{
const size_t index = get_random_index(signalsCount);
connection conn = signals.at(index).connect(slot);
{
std::lock_guard lock(connectionsMutex);
connections.emplace_back(conn);
}
}
});
for (unsigned fti = 0; fti < fireThreadCount; ++fti)
{
threads.emplace_back([&] {
for (unsigned fi = 0; fi < fireCountPerThread; ++fi)
{
const size_t index = get_random_index(signalsCount);
signals.at(index)();
}
});
}
for (auto& thread : threads)
{
thread.join();
}
}
}
@@ -1,3 +0,0 @@
custom_add_test_from_dir(libfastsignals_unit_tests libfastsignals)
target_include_directories(libfastsignals_unit_tests PRIVATE "${CMAKE_SOURCE_DIR}/tests")
@@ -1,618 +0,0 @@
#include "catch2/catch.hpp"
#include <fastsignals/function.h>
#include <array>
using namespace fastsignals;
namespace
{
int Abs(int x)
{
return x >= 0 ? x : -x;
}
int Sum(int a, int b)
{
return a + b;
}
void InplaceAbs(int& x)
{
x = Abs(x);
}
std::string GetStringHello()
{
return "hello";
}
class AbsFunctor
{
public:
int operator()(int x) const
{
return Abs(x);
}
};
class SumFunctor
{
public:
int operator()(int a, int b) const
{
return Sum(a, b);
}
};
class InplaceAbsFunctor
{
public:
void operator()(int& x) /* non-const */
{
if (m_calledOnce)
{
abort();
}
m_calledOnce = true;
InplaceAbs(x);
}
private:
bool m_calledOnce = false;
};
class GetStringFunctor
{
public:
explicit GetStringFunctor(const std::string& value)
: m_value(value)
{
}
std::string operator()() /* non-const */
{
if (m_calledOnce)
{
abort();
}
m_calledOnce = true;
return m_value;
}
private:
bool m_calledOnce = false;
std::string m_value;
};
} // namespace
TEST_CASE("Can use free function with 1 argument", "[function]")
{
function<int(int)> fn = Abs;
REQUIRE(fn(10) == 10);
REQUIRE(fn(-10) == 10);
REQUIRE(fn(0) == 0);
}
TEST_CASE("Can use free function with 2 arguments", "[function]")
{
function<int(int, int)> fn = Sum;
REQUIRE(fn(10, 5) == 15);
REQUIRE(fn(-10, 0) == -10);
}
TEST_CASE("Can use free function without arguments", "[function]")
{
function<std::string()> fn = GetStringHello;
REQUIRE(fn() == "hello");
}
TEST_CASE("Can use free function without return value", "[function]")
{
function<void(int&)> fn = InplaceAbs;
int a = -10;
fn(a);
REQUIRE(a == 10);
}
TEST_CASE("Can use lambda with 1 argument", "[function]")
{
function<int(int)> fn = [](int value) {
return Abs(value);
};
REQUIRE(fn(10) == 10);
REQUIRE(fn(-10) == 10);
REQUIRE(fn(0) == 0);
}
TEST_CASE("Can use lambda with 2 arguments", "[function]")
{
function<int(int, int)> fn = [](auto&& a, auto&& b) {
return Sum(a, b);
};
REQUIRE(fn(10, 5) == 15);
REQUIRE(fn(-10, 0) == -10);
}
TEST_CASE("Can use lambda without arguments", "[function]")
{
function<std::string()> fn = [] {
return GetStringHello();
};
REQUIRE(fn() == "hello");
}
TEST_CASE("Can use lambda without return value", "[function]")
{
bool calledOnce = false;
function<void(int&)> fn = [calledOnce](auto& value) mutable {
if (calledOnce)
{
abort();
}
calledOnce = true;
InplaceAbs(value);
};
int a = -10;
fn(a);
REQUIRE(a == 10);
}
TEST_CASE("Can use functor with 1 argument", "[function]")
{
function<int(int)> fn = AbsFunctor();
REQUIRE(fn(10) == 10);
REQUIRE(fn(-10) == 10);
REQUIRE(fn(0) == 0);
}
TEST_CASE("Can use functor with 2 arguments", "[function]")
{
function<int(int, int)> fn = SumFunctor();
REQUIRE(fn(10, 5) == 15);
REQUIRE(fn(-10, 0) == -10);
}
TEST_CASE("Can use functor without arguments", "[function]")
{
function<std::string()> fn = GetStringFunctor("hello");
REQUIRE(fn() == "hello");
}
TEST_CASE("Can use functor without return value", "[function]")
{
function<void(int&)> fn = InplaceAbsFunctor();
int a = -10;
fn(a);
REQUIRE(a == 10);
}
TEST_CASE("Can construct function with cons std::function<>&", "[function]")
{
using BoolCallback = std::function<void(bool succeed)>;
bool value = false;
const BoolCallback& cb = [&value](bool succeed) {
value = succeed;
};
function<void(bool)> fn = cb;
fn(true);
REQUIRE(value == true);
fn(false);
REQUIRE(value == false);
fn(true);
REQUIRE(value == true);
}
TEST_CASE("Can copy function", "[function]")
{
unsigned calledCount = 0;
bool value = false;
function<void(bool)> callback = [&](bool gotValue) {
++calledCount;
value = gotValue;
};
auto callback2 = callback;
REQUIRE(calledCount == 0);
CHECK(!value);
callback(true);
REQUIRE(calledCount == 1);
CHECK(value);
callback2(false);
REQUIRE(calledCount == 2);
CHECK(!value);
}
TEST_CASE("Can move function", "[function]")
{
bool called = false;
function<void()> callback = [&] {
called = true;
};
auto callback2(std::move(callback));
REQUIRE_THROWS(callback());
REQUIRE(!called);
callback2();
REQUIRE(called);
}
TEST_CASE("Works when copying self", "[function]")
{
bool called = false;
function<void()> callback = [&] {
called = true;
};
callback = callback;
callback();
REQUIRE(called);
}
TEST_CASE("Can release packed function", "[function]")
{
function<int()> iota = [v = 0]() mutable {
return v++;
};
REQUIRE(iota() == 0);
auto packedFn = std::move(iota).release();
REQUIRE_THROWS_AS(iota(), std::bad_function_call);
auto&& proxy = packedFn.get<int()>();
REQUIRE(proxy() == 1);
REQUIRE(proxy() == 2);
}
TEST_CASE("Function copy has its own packed function", "[function]")
{
function<int()> iota = [v = 0]() mutable {
return v++;
};
REQUIRE(iota() == 0);
auto iotaCopy(iota);
REQUIRE(iota() == 1);
REQUIRE(iota() == 2);
REQUIRE(iotaCopy() == 1);
REQUIRE(iotaCopy() == 2);
}
TEST_CASE("can work with callables that have vtable", "[function]")
{
class Base
{
};
class Interface : public Base
{
public:
virtual ~Interface() = default;
virtual void operator()() const = 0;
};
class Class : public Interface
{
public:
Class(bool* destructorCalled)
: m_destructorCalled(destructorCalled)
{
}
~Class()
{
*m_destructorCalled = true;
}
void operator()() const override
{
}
bool* m_destructorCalled = nullptr;
};
bool destructorCalled = false;
{
function<void()> f = Class(&destructorCalled);
f();
auto packed = f.release();
destructorCalled = false;
}
CHECK(destructorCalled);
}
TEST_CASE("can work with callables with virtual inheritance", "[function]")
{
struct A
{
void operator()() const
{
m_called = true;
}
~A()
{
*m_destructorCalled = true;
}
mutable bool m_called = false;
bool* m_destructorCalled = nullptr;
};
struct B : public virtual A
{
};
struct C : public virtual A
{
};
struct D : virtual public B
, virtual public C
{
D(bool* destructorCalled)
{
m_destructorCalled = destructorCalled;
}
using A::operator();
};
bool destructorCalled = false;
{
function<void()> f = D(&destructorCalled);
f();
auto packed = f.release();
destructorCalled = false;
}
CHECK(destructorCalled);
}
TEST_CASE("uses copy constructor if callable's move constructor throws", "[function]")
{
struct Callable
{
Callable() = default;
Callable(Callable&&)
{
throw std::runtime_error("throw");
}
Callable(const Callable& other) = default;
void operator()() const
{
}
};
Callable c;
function<void()> f(c);
auto f2 = std::move(f);
f2();
CHECK_THROWS(f());
}
TEST_CASE("uses move constructor if it is noexcept", "[function]")
{
struct Callable
{
Callable() = default;
Callable(Callable&& other) noexcept = default;
Callable(const Callable&)
{
throw std::runtime_error("throw");
}
void operator()() const
{
}
};
Callable c;
function<void()> f(std::move(c));
auto f2 = std::move(f);
f2();
CHECK_THROWS(f());
}
TEST_CASE("can copy and move empty function", "[function]")
{
function<void()> f;
auto f2 = f;
auto f3 = std::move(f);
}
TEST_CASE("properly copies callable on assignment", "[function]")
{
struct Callable
{
Callable(int& aliveCounter)
: m_aliveCounter(&aliveCounter)
{
++*m_aliveCounter;
}
Callable(const Callable& other)
: m_aliveCounter(other.m_aliveCounter)
{
if (m_aliveCounter)
{
++*m_aliveCounter;
}
}
Callable(Callable&& other) noexcept
: m_aliveCounter(other.m_aliveCounter)
{
other.m_aliveCounter = nullptr;
}
~Callable()
{
if (m_aliveCounter)
{
--*m_aliveCounter;
}
}
void operator()() const
{
}
int* m_aliveCounter = nullptr;
};
int aliveCounter1 = 0;
int aliveCounter2 = 0;
function<void()> f = Callable(aliveCounter1);
function<void()> f2 = Callable(aliveCounter2);
CHECK(aliveCounter1 == 1);
CHECK(aliveCounter2 == 1);
f = f2;
CHECK(aliveCounter1 == 0);
CHECK(aliveCounter2 == 2);
f = function<void()>();
f2 = function<void()>();
CHECK(aliveCounter1 == 0);
CHECK(aliveCounter2 == 0);
}
TEST_CASE("copy assignment operator provides strong exception safety", "[function]")
{
struct State
{
int callCount = 0;
bool throwOnCopy = false;
};
struct Callable
{
Callable(State& state)
: state(&state)
{
}
void operator()()
{
++state->callCount;
}
Callable(const Callable& other)
: state(other.state)
{
if (state->throwOnCopy)
{
throw std::runtime_error("throw on request");
}
}
State* state = nullptr;
};
static_assert(!detail::can_use_inplace_buffer<Callable>);
State srcState;
State dstState;
function<void()> srcFn(Callable{ srcState });
function<void()> dstFn(Callable{ dstState });
srcFn();
dstFn();
REQUIRE(srcState.callCount == 1);
REQUIRE(dstState.callCount == 1);
srcState.throwOnCopy = true;
REQUIRE_THROWS_AS(dstFn = srcFn, std::runtime_error);
// srcFn and dstFn must not be emptied even if assignment throws
REQUIRE_NOTHROW(srcFn());
REQUIRE_NOTHROW(dstFn());
// srcFn and dstFn must keep their state
REQUIRE(srcState.callCount == 2);
REQUIRE(dstState.callCount == 2);
// The next copy will succeed
srcState.throwOnCopy = false;
REQUIRE_NOTHROW(dstFn = srcFn);
// Both functions are usable
REQUIRE_NOTHROW(srcFn());
REQUIRE_NOTHROW(dstFn());
// After assignment, dstFn and srcFn refer the same state - srcState
REQUIRE(srcState.callCount == 4);
REQUIRE(dstState.callCount == 2);
}
TEST_CASE("assignment of variously allocated functions", "[function]")
{
int heapCalls = 0;
auto onHeap = [&heapCalls, largeVar = std::array<std::string, 1000>()]() mutable {
std::fill(largeVar.begin(), largeVar.end(), "large string to be allocated on heap instead of stack");
++heapCalls;
};
int stackCalls = 0;
auto onStack = [&stackCalls] {
++stackCalls;
};
static_assert(detail::can_use_inplace_buffer<detail::function_proxy_impl<decltype(onStack), void>>);
static_assert(!detail::can_use_inplace_buffer<detail::function_proxy_impl<decltype(onHeap), void>>);
using Fn = function<void()>;
{
Fn heap(onHeap);
Fn stack(onStack);
heap = stack;
heap();
REQUIRE(stackCalls == 1);
REQUIRE(heapCalls == 0);
}
{
Fn heap(onHeap);
Fn stack(onStack);
stack = heap;
stack();
REQUIRE(stackCalls == 1);
REQUIRE(heapCalls == 1);
}
{
Fn heap(onHeap);
Fn heap1(onHeap);
heap = heap1;
heap();
REQUIRE(stackCalls == 1);
REQUIRE(heapCalls == 2);
}
{
Fn stack(onStack);
Fn stack1(onStack);
stack = stack1;
stack();
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 2);
}
{
Fn heap(onHeap);
Fn empty;
heap = empty;
REQUIRE_THROWS(heap());
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 2);
}
{
Fn stack(onStack);
Fn empty;
stack = empty;
REQUIRE_THROWS(stack());
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 2);
}
{
Fn empty;
Fn heap(onHeap);
empty = heap;
empty();
REQUIRE(stackCalls == 2);
REQUIRE(heapCalls == 3);
}
{
Fn empty;
Fn stack(onStack);
empty = stack;
empty();
REQUIRE(stackCalls == 3);
REQUIRE(heapCalls == 3);
}
{
Fn empty;
Fn empty1;
empty = empty1;
REQUIRE_THROWS(empty());
REQUIRE(stackCalls == 3);
REQUIRE(heapCalls == 3);
}
}

Some files were not shown because too many files have changed in this diff Show More