Compare commits
328 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c7935c755 | |||
| b9c11dc75b | |||
| 99f27d4b9f | |||
| 0b4e10ca4d | |||
| e5f717ba50 | |||
| b3f5a35ef7 | |||
| dae6cc2481 | |||
| 37b8ba683b | |||
| 86f512e2c3 | |||
| 2fdda0c4e0 | |||
| 2468a26f01 | |||
| f7761c5b14 | |||
| b56fba2ee6 | |||
| d80801c07c | |||
| 728ae02651 | |||
| efbf8eafb7 | |||
| 1853a3b0a1 | |||
| 1d81ac4e2a | |||
| 54b84ffa1b | |||
| ad2f530875 | |||
| 43696df1b1 | |||
| b7e3bb1f7c | |||
| fc2ad996f4 | |||
| ea9039cd30 | |||
| b16ec077ba | |||
| b98dfeef70 | |||
| 4c844ca5f5 | |||
| 6c8917d9c8 | |||
| 48ddc33bd3 | |||
| 39750371ad | |||
| ab65ea8fe4 | |||
| f0930dbd83 | |||
| 3672610617 | |||
| 322f1348e3 | |||
| 3f24fca729 | |||
| 73f5f332fb | |||
| 11756c1cd4 | |||
| 2249b8cdd6 | |||
| ed6bf31b77 | |||
| c269631319 | |||
| 43c88854d0 | |||
| 07f5b2362e | |||
| b62a27ce6b | |||
| bab554670d | |||
| 8da1881389 | |||
| 20623a7757 | |||
| 1dbcd2f8df | |||
| 2df9e6d0d5 | |||
| 99e25d353a | |||
| 0f8e601e78 | |||
| 2d3917c62f | |||
| 7168aba55d | |||
| 730065a94c | |||
| 5e9b228ce1 | |||
| e95b18012e | |||
| e54f09e5e7 | |||
| 737b6e27be | |||
| c1d394802e | |||
| 2cf7cc761a | |||
| 4a9f2ff24a | |||
| 62181c7e74 | |||
| 90612b05a2 | |||
| a896929f1e | |||
| 39d2151eca | |||
| bffe90dce6 | |||
| 3901c8f1de | |||
| 1ab014c196 | |||
| 05e26f3d03 | |||
| e99a06f402 | |||
| d5ac6a07e6 | |||
| c9ca3d01e3 | |||
| f3511baa7e | |||
| 1c374c68fa | |||
| e3e04387a5 | |||
| 3a405bbf6b | |||
| 38dd102f77 | |||
| 6fd72e00cd | |||
| ea048829f7 | |||
| aca2eeb173 | |||
| 01c42b34d9 | |||
| dbf87623b2 | |||
| 173734c902 | |||
| cfe36321d1 | |||
| 4e187e83bf | |||
| 525ce8e056 | |||
| 1b678a2ec6 | |||
| 6e08c67a3b | |||
| 5ae095a0a8 | |||
| d21f01ce3b | |||
| dde6efaa2d | |||
| d495562e56 | |||
| bd6dbbcf00 | |||
| b67b74181c | |||
| 0c48516c57 | |||
| ec3775291a | |||
| 3b83c90c3b | |||
| 749f6cbc53 | |||
| 9e58942004 | |||
| 6c1bd0f21b | |||
| 7310a88866 | |||
| d4f4691f1f | |||
| 3b1bc25870 | |||
| 254ee35bc2 | |||
| 4e74fe97ed | |||
| 456534cce6 | |||
| e1201b35a1 | |||
| c7489597bc | |||
| 76b1cb43cc | |||
| 11d8e6a411 | |||
| d172914c5d | |||
| b33cadca1b | |||
| 165e58a7b9 | |||
| fb807c5a62 | |||
| 1a537852ae | |||
| 0b370963e1 | |||
| 3bf2c5a019 | |||
| 014851827c | |||
| 143e16bd44 | |||
| 6b7262bba9 | |||
| ef5b86afa4 | |||
| 10680272e1 | |||
| 17086d6ff2 | |||
| d03d8589f3 | |||
| 0e490cae46 | |||
| 4559d078e4 | |||
| 7c7cb1a783 | |||
| 5c8ba72158 | |||
| 16bead131a | |||
| 3283b6e4e4 | |||
| cf67fed6bc | |||
| 39c9bb67ef | |||
| b59533a94d | |||
| f16e9f69e5 | |||
| 1387e274f2 | |||
| 3090218976 | |||
| 82f04f6303 | |||
| 56fab21375 | |||
| 83b96c6c36 | |||
| 29de8ace0e | |||
| 3243f85145 | |||
| 282768a5c8 | |||
| ac8ed20250 | |||
| 8b257d701f | |||
| 1f642f553b | |||
| bfda3e194f | |||
| f216df1126 | |||
| 2e5a190149 | |||
| e83131ec20 | |||
| 83a821c68e | |||
| 9b1e26cd87 | |||
| 7062b0511e | |||
| 9a70a2838a | |||
| e48a90dbde | |||
| 234ddb951a | |||
| 685b90c38a | |||
| 1df4fe0ffd | |||
| 528a1f5c6e | |||
| 1b90efd21b | |||
| 0308a1b849 | |||
| c6de3d88be | |||
| 9e59e48e80 | |||
| 7a161b2f9e | |||
| 5e506d0b18 | |||
| 448613f3c4 | |||
| 76a23ec105 | |||
| 92165ac37d | |||
| 3922a674da | |||
| 3dc4f11b2b | |||
| 81956de275 | |||
| 5763b1df60 | |||
| 41a30467a5 | |||
| b2e8b5c3e2 | |||
| b893924cbe | |||
| 3e8e2faf62 | |||
| feca3a8436 | |||
| 84a2662171 | |||
| afbce0a6a2 | |||
| b77c5da78b | |||
| de7c105a1a | |||
| 6aa7f99e1e | |||
| 522c2b116b | |||
| e1128e3491 | |||
| 9fcef20600 | |||
| f72e3439af | |||
| 135bd360f0 | |||
| 2b5213863b | |||
| 12438b1999 | |||
| 7f785d64cb | |||
| cdd6a0a719 | |||
| b6f5216b70 | |||
| 09127570d3 | |||
| 898c45cb95 | |||
| 9483062cbd | |||
| d8f3bedbd9 | |||
| 9c5a6ff782 | |||
| 9ee1295842 | |||
| c5c336927d | |||
| 683fc7986c | |||
| 7b37c5612b | |||
| c7e3fbe738 | |||
| 386e375e1d | |||
| b526751f97 | |||
| 70645e2f46 | |||
| 5ea6fa8eb9 | |||
| 2d77ea2221 | |||
| 56d6bbe4b6 | |||
| 2596ab8bcb | |||
| 53767601ea | |||
| 08dabacfed | |||
| 9b934a45db | |||
| a0a8d7760b | |||
| 4962bbbffe | |||
| 7c6d70ab02 | |||
| 30e94d0b3f | |||
| 70c69bbebc | |||
| 1cd0927a07 | |||
| e5b5fb38a4 | |||
| e55305fb25 | |||
| be96c07587 | |||
| b5dee9832f | |||
| 69f6f9e4b0 | |||
| 9c24507531 | |||
| ada3b7c97e | |||
| bc5b7d66c1 | |||
| 9931e01be7 | |||
| 7176a5c3bb | |||
| ac39784aac | |||
| da432a77af | |||
| e6e0d03034 | |||
| c5387aba66 | |||
| 1d739f9773 | |||
| 7c0c421401 | |||
| 45c5b33db5 | |||
| 501f29a247 | |||
| 57a1ac5812 | |||
| 680ccc0ecf | |||
| 7a7cba5587 | |||
| c90f7d67df | |||
| 402032c230 | |||
| 4ba4d1b848 | |||
| 024e904c9a | |||
| 74879b6738 | |||
| e76fdd82dc | |||
| a7486c6fa9 | |||
| 4a331340fe | |||
| e3d0fe6bb6 | |||
| 8c36c5390a | |||
| 2ed6528d61 | |||
| 1c54777405 | |||
| 4cd7fdc2df | |||
| c637b532f1 | |||
| d5da0227ee | |||
| 3446b933cb | |||
| 6f999f8885 | |||
| 95c547bd5a | |||
| 7aed77aaec | |||
| aa02543896 | |||
| a6da44aa03 | |||
| c8f629b8b4 | |||
| b71344c07b | |||
| 8e73bddec1 | |||
| aa010759d3 | |||
| 383a51075c | |||
| aeb6fca064 | |||
| 32bfee0ada | |||
| cb4d6b9cb7 | |||
| d2a99e83f7 | |||
| 78df9dd937 | |||
| 0b92c77685 | |||
| b73a2cca0d | |||
| 205d024b74 | |||
| fc317b16b0 | |||
| 3b80c25339 | |||
| ccd3e778ff | |||
| 7bf96e2ef0 | |||
| 387c3be2bf | |||
| 2c775840da | |||
| 14d93d24fb | |||
| c7b3353414 | |||
| a469c45a2d | |||
| 4e6bb3f524 | |||
| ba5652e046 | |||
| 40cd4fe772 | |||
| 5f68507f6d | |||
| ae864aedb4 | |||
| d4f2c904f8 | |||
| 2ff62e9727 | |||
| 302fbc6765 | |||
| 456bc178c6 | |||
| 66ed85bb19 | |||
| cd2e26bbea | |||
| 6058a47f58 | |||
| 092ae3f8ea | |||
| 3971ce2bf7 | |||
| 69498be228 | |||
| 08cceec9cf | |||
| 3c6cdb17e0 | |||
| 15de64e30b | |||
| d936ba474c | |||
| b44af1f768 | |||
| f308fb92a3 | |||
| 98a1dd2f14 | |||
| 0887aa188b | |||
| 9a4cedf1a4 | |||
| 894e1d9ae2 | |||
| 37f014998c | |||
| 1d21e56ee1 | |||
| c43b615301 | |||
| 125d3b65c7 | |||
| 9946819e9f | |||
| 06d55d50ce | |||
| 1678981185 | |||
| 4e2d2e3206 | |||
| 3fc8528692 | |||
| 6508023e95 | |||
| ce6d9f361a | |||
| 95fe960485 | |||
| 49d4375f70 | |||
| b0ffa6728c | |||
| 38fccbacf6 | |||
| a8ba9b82a5 | |||
| 647877384a | |||
| a60b4698be | |||
| 1a7fa655f3 | |||
| 6d023afaac | |||
| 4642fecba3 | |||
| 2ae9f89982 | |||
| a6ec30a8fc |
@@ -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
|
||||
@@ -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
|
||||
@@ -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: |
|
||||
|
||||
@@ -54,21 +54,21 @@ jobs:
|
||||
|
||||
Ubuntu:
|
||||
needs: [Prepare]
|
||||
if: "!startsWith(github.head_ref, 'refs/heads/backport-')"
|
||||
if: "!startsWith(github.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-')"
|
||||
if: "!startsWith(github.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-')"
|
||||
if: "!startsWith(github.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
|
||||
|
||||
@@ -28,12 +28,12 @@ jobs:
|
||||
)
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN_FOR_CI_RUNNERS }}
|
||||
|
||||
- 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 }}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 }}'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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: |
|
||||
|
||||
@@ -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,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**
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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
@@ -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 "rc2") # 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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
macro(SetupLark)
|
||||
# ------------------------------ Lark ------------------------------
|
||||
|
||||
find_package(LARK MODULE REQUIRED)
|
||||
message(STATUS "Found Lark: version ${LARK_VERSION}")
|
||||
|
||||
endmacro()
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
@@ -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."
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
Name: freecad
|
||||
Epoch: 1
|
||||
Version: 1.1.0~dev
|
||||
Version: 1.1.0~rc2
|
||||
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
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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" \
|
||||
|
||||
@@ -31,4 +31,4 @@ else
|
||||
MAIN="$HERE/usr/bin/freecad"
|
||||
fi
|
||||
|
||||
exec "${MAIN}" "$@"
|
||||
"${MAIN}" "$@"
|
||||
|
||||
@@ -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)
|
||||
|
||||
+4127
-4304
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,15 @@
|
||||
[workspace]
|
||||
channels = [
|
||||
"https://prefix.dev/pixi-build-backends",
|
||||
"https://prefix.dev/conda-forge",
|
||||
"freecad",
|
||||
"conda-forge",
|
||||
]
|
||||
platforms = ["linux-64", "linux-aarch64", "osx-64", "osx-arm64", "win-64"]
|
||||
preview = ["pixi-build"]
|
||||
|
||||
[package]
|
||||
name = "freecad"
|
||||
version = "1.1.0dev"
|
||||
version = "1.1.0rc2"
|
||||
homepage = "https://freecad.org"
|
||||
repository = "https://github.com/FreeCAD/FreeCAD"
|
||||
description = "FreeCAD"
|
||||
@@ -28,12 +29,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]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
context:
|
||||
version: "1.1.0dev"
|
||||
version: "1.1.0rc2"
|
||||
|
||||
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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "FreeCAD"
|
||||
version = "1.0.0"
|
||||
description = "pixi instructions for FreeCAD"
|
||||
authors = ["looooo <sppedflyer@gmail.com>"]
|
||||
channels = ["conda-forge"]
|
||||
channels = ["freecad", "conda-forge"]
|
||||
platforms = [ "linux-64", "linux-aarch64", "osx-64", "osx-arm64", "win-64" ]
|
||||
requires-pixi = ">=0.48"
|
||||
|
||||
@@ -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 = "*"
|
||||
@@ -150,7 +145,7 @@ sed = "*"
|
||||
sed = "*"
|
||||
|
||||
[target.win-64.dependencies]
|
||||
pthreads-win32 = "*"
|
||||
winpthreads-devel = "*"
|
||||
|
||||
[pypi-dependencies]
|
||||
freecad-stubs = "*"
|
||||
|
||||
Vendored
-2
@@ -1,5 +1,3 @@
|
||||
add_subdirectory(FastSignals)
|
||||
|
||||
# Build SalomeMesh for all Platforms since heavily patched
|
||||
if (BUILD_SMESH)
|
||||
add_subdirectory(salomesmesh)
|
||||
|
||||
-102
@@ -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
|
||||
...
|
||||
@@ -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
@@ -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()
|
||||
Vendored
-21
@@ -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.
|
||||
Vendored
-17
@@ -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
|
||||
|
||||
[](https://travis-ci.org/ispringteam/FastSignals)
|
||||
[](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
@@ -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
@@ -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.
|
||||
@@ -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<> 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);
|
||||
}
|
||||
```
|
||||
@@ -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
|
||||
-164
@@ -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
|
||||
-17976
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"
|
||||
-124
@@ -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);
|
||||
}
|
||||
}
|
||||
-132
@@ -1,132 +0,0 @@
|
||||
#include "catch2/catch.hpp"
|
||||
#include <fastsignals/bind_weak.h>
|
||||
|
||||
using namespace fastsignals;
|
||||
|
||||
namespace
|
||||
{
|
||||
class Testbed
|
||||
{
|
||||
public:
|
||||
Testbed(unsigned& counter)
|
||||
: m_pCounter(&counter)
|
||||
{
|
||||
}
|
||||
|
||||
void IncrementNonConst()
|
||||
{
|
||||
++(*m_pCounter);
|
||||
}
|
||||
|
||||
void IncrementsConst() const
|
||||
{
|
||||
++(*m_pCounter);
|
||||
}
|
||||
|
||||
int ReflectInt(int value) const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned* m_pCounter = nullptr;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("can bind const methods", "[bind_weak]")
|
||||
{
|
||||
unsigned counter = 0;
|
||||
auto pSharedBed = std::make_shared<Testbed>(counter);
|
||||
auto boundFn = bind_weak(&Testbed::IncrementNonConst, pSharedBed);
|
||||
REQUIRE(counter == 0u);
|
||||
boundFn();
|
||||
REQUIRE(counter == 1u);
|
||||
boundFn();
|
||||
REQUIRE(counter == 2u);
|
||||
pSharedBed = nullptr;
|
||||
boundFn();
|
||||
REQUIRE(counter == 2u);
|
||||
boundFn();
|
||||
REQUIRE(counter == 2u);
|
||||
}
|
||||
|
||||
TEST_CASE("can bind non const methods", "[bind_weak]")
|
||||
{
|
||||
unsigned counter = 0;
|
||||
auto pSharedBed = std::make_shared<Testbed>(counter);
|
||||
auto boundFn = bind_weak(&Testbed::IncrementsConst, pSharedBed);
|
||||
REQUIRE(counter == 0u);
|
||||
boundFn();
|
||||
REQUIRE(counter == 1u);
|
||||
boundFn();
|
||||
REQUIRE(counter == 2u);
|
||||
pSharedBed = nullptr;
|
||||
boundFn();
|
||||
REQUIRE(counter == 2u);
|
||||
boundFn();
|
||||
REQUIRE(counter == 2u);
|
||||
}
|
||||
|
||||
TEST_CASE("can bind method with argument value", "[bind_weak]")
|
||||
{
|
||||
unsigned counter = 0;
|
||||
auto pSharedBed = std::make_shared<Testbed>(counter);
|
||||
auto boundFn = bind_weak(&Testbed::ReflectInt, pSharedBed, 42);
|
||||
REQUIRE(boundFn() == 42);
|
||||
REQUIRE(boundFn() == 42);
|
||||
pSharedBed = nullptr;
|
||||
REQUIRE(boundFn() == 0);
|
||||
REQUIRE(boundFn() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("copies value when bind method with argument const reference value", "[bind_weak]")
|
||||
{
|
||||
unsigned counter = 0;
|
||||
auto pSharedBed = std::make_shared<Testbed>(counter);
|
||||
auto makeBoundFn = [&]() {
|
||||
int value = 15;
|
||||
const int& valueRef = value;
|
||||
auto result = bind_weak(&Testbed::ReflectInt, pSharedBed, valueRef);
|
||||
value = 25;
|
||||
return result;
|
||||
};
|
||||
|
||||
auto boundFn = makeBoundFn();
|
||||
REQUIRE(boundFn(42) == 15);
|
||||
REQUIRE(boundFn(42) == 15);
|
||||
pSharedBed = nullptr;
|
||||
REQUIRE(boundFn(42) == 0);
|
||||
REQUIRE(boundFn(42) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("copies value when bind method with argument reference value", "[bind_weak]")
|
||||
{
|
||||
unsigned counter = 0;
|
||||
auto pSharedBed = std::make_shared<Testbed>(counter);
|
||||
auto makeBoundFn = [&]() {
|
||||
int value = 15;
|
||||
int& valueRef = value;
|
||||
auto result = bind_weak(&Testbed::ReflectInt, pSharedBed, valueRef);
|
||||
valueRef = 25;
|
||||
return result;
|
||||
};
|
||||
|
||||
auto boundFn = makeBoundFn();
|
||||
REQUIRE(boundFn(42) == 15);
|
||||
REQUIRE(boundFn(42) == 15);
|
||||
pSharedBed = nullptr;
|
||||
REQUIRE(boundFn(42) == 0);
|
||||
REQUIRE(boundFn(42) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("can bind method with placeholder", "[bind_weak]")
|
||||
{
|
||||
unsigned counter = 0;
|
||||
auto pSharedBed = std::make_shared<Testbed>(counter);
|
||||
auto boundFn = bind_weak(&Testbed::ReflectInt, pSharedBed, std::placeholders::_1);
|
||||
REQUIRE(boundFn(42) == 42);
|
||||
REQUIRE(boundFn(42) == 42);
|
||||
pSharedBed = nullptr;
|
||||
REQUIRE(boundFn(42) == 0);
|
||||
REQUIRE(boundFn(42) == 0);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch2/catch.hpp"
|
||||
@@ -1,707 +0,0 @@
|
||||
#include "catch2/catch.hpp"
|
||||
#include <fastsignals/signal.h>
|
||||
#include <string>
|
||||
|
||||
using namespace fastsignals;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class T>
|
||||
class any_of_combiner
|
||||
{
|
||||
public:
|
||||
static_assert(std::is_same_v<T, bool>);
|
||||
|
||||
using result_type = bool;
|
||||
|
||||
template <class TRef>
|
||||
void operator()(TRef&& value)
|
||||
{
|
||||
m_result = m_result || bool(value);
|
||||
}
|
||||
|
||||
result_type get_value() const
|
||||
{
|
||||
return m_result;
|
||||
}
|
||||
|
||||
private:
|
||||
result_type m_result = {};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
TEST_CASE("Can connect a few slots and emit", "[signal]")
|
||||
{
|
||||
signal<void(int)> valueChanged;
|
||||
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
valueChanged.connect([&value1](int value) {
|
||||
value1 = value;
|
||||
});
|
||||
valueChanged.connect([&value2](int value) {
|
||||
value2 = value;
|
||||
});
|
||||
REQUIRE(value1 == 0);
|
||||
REQUIRE(value2 == 0);
|
||||
|
||||
valueChanged(10);
|
||||
REQUIRE(value1 == 10);
|
||||
REQUIRE(value2 == 10);
|
||||
}
|
||||
|
||||
TEST_CASE("Can safely pass rvalues", "[signal]")
|
||||
{
|
||||
const std::string expected = "If the type T is a reference type, provides the member typedef type which is the type referred to by T. Otherwise type is T.";
|
||||
std::string passedValue = expected;
|
||||
signal<void(std::string)> valueChanged;
|
||||
|
||||
std::string value1;
|
||||
std::string value2;
|
||||
valueChanged.connect([&value1](std::string value) {
|
||||
value1 = value;
|
||||
});
|
||||
valueChanged.connect([&value2](std::string value) {
|
||||
value2 = value;
|
||||
});
|
||||
|
||||
valueChanged(std::move(passedValue));
|
||||
REQUIRE(value1 == expected);
|
||||
REQUIRE(value2 == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("Can pass mutable ref", "[signal]")
|
||||
{
|
||||
const std::string expected = "If the type T is a reference type, provides the member typedef type which is the type referred to by T. Otherwise type is T.";
|
||||
signal<void(std::string&)> valueChanged;
|
||||
|
||||
std::string passedValue;
|
||||
valueChanged.connect([expected](std::string& value) {
|
||||
value = expected;
|
||||
});
|
||||
valueChanged(passedValue);
|
||||
|
||||
REQUIRE(passedValue == expected);
|
||||
}
|
||||
|
||||
TEST_CASE("Can disconnect slot with explicit call", "[signal]")
|
||||
{
|
||||
signal<void(int)> valueChanged;
|
||||
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
int value3 = 0;
|
||||
auto conn1 = valueChanged.connect([&value1](int value) {
|
||||
value1 = value;
|
||||
});
|
||||
auto conn2 = valueChanged.connect([&value2](int value) {
|
||||
value2 = value;
|
||||
});
|
||||
valueChanged.connect([&value3](int value) {
|
||||
value3 = value;
|
||||
});
|
||||
REQUIRE(value1 == 0);
|
||||
REQUIRE(value2 == 0);
|
||||
REQUIRE(value3 == 0);
|
||||
|
||||
valueChanged(10);
|
||||
REQUIRE(value1 == 10);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == 10);
|
||||
|
||||
conn2.disconnect();
|
||||
valueChanged(-99);
|
||||
REQUIRE(value1 == -99);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == -99);
|
||||
|
||||
conn1.disconnect();
|
||||
valueChanged(17);
|
||||
REQUIRE(value1 == -99);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == 17);
|
||||
}
|
||||
|
||||
TEST_CASE("Can disconnect slot with scoped_connection", "[signal]")
|
||||
{
|
||||
signal<void(int)> valueChanged;
|
||||
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
int value3 = 0;
|
||||
{
|
||||
scoped_connection conn1 = valueChanged.connect([&value1](int value) {
|
||||
value1 = value;
|
||||
});
|
||||
{
|
||||
scoped_connection conn2 = valueChanged.connect([&value2](int value) {
|
||||
value2 = value;
|
||||
});
|
||||
valueChanged.connect([&value3](int value) {
|
||||
value3 = value;
|
||||
});
|
||||
REQUIRE(value1 == 0);
|
||||
REQUIRE(value2 == 0);
|
||||
REQUIRE(value3 == 0);
|
||||
|
||||
valueChanged(10);
|
||||
REQUIRE(value1 == 10);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == 10);
|
||||
}
|
||||
|
||||
// conn2 disconnected.
|
||||
valueChanged(-99);
|
||||
REQUIRE(value1 == -99);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == -99);
|
||||
}
|
||||
|
||||
// conn1 disconnected.
|
||||
valueChanged(17);
|
||||
REQUIRE(value1 == -99);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == 17);
|
||||
}
|
||||
|
||||
TEST_CASE("Can disconnect all", "[signal]")
|
||||
{
|
||||
signal<void(int)> valueChanged;
|
||||
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
int value3 = 0;
|
||||
valueChanged.connect([&value1](int value) {
|
||||
value1 = value;
|
||||
});
|
||||
valueChanged.connect([&value2](int value) {
|
||||
value2 = value;
|
||||
});
|
||||
valueChanged.connect([&value3](int value) {
|
||||
value3 = value;
|
||||
});
|
||||
REQUIRE(value1 == 0);
|
||||
REQUIRE(value2 == 0);
|
||||
REQUIRE(value3 == 0);
|
||||
|
||||
valueChanged(63);
|
||||
REQUIRE(value1 == 63);
|
||||
REQUIRE(value2 == 63);
|
||||
REQUIRE(value3 == 63);
|
||||
|
||||
valueChanged.disconnect_all_slots();
|
||||
valueChanged(101);
|
||||
REQUIRE(value1 == 63);
|
||||
REQUIRE(value2 == 63);
|
||||
REQUIRE(value3 == 63);
|
||||
}
|
||||
|
||||
TEST_CASE("Can disconnect inside slot", "[signal]")
|
||||
{
|
||||
signal<void(int)> valueChanged;
|
||||
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
int value3 = 0;
|
||||
connection conn2;
|
||||
valueChanged.connect([&value1](int value) {
|
||||
value1 = value;
|
||||
});
|
||||
conn2 = valueChanged.connect([&](int value) {
|
||||
value2 = value;
|
||||
conn2.disconnect();
|
||||
});
|
||||
valueChanged.connect([&value3](int value) {
|
||||
value3 = value;
|
||||
});
|
||||
REQUIRE(value1 == 0);
|
||||
REQUIRE(value2 == 0);
|
||||
REQUIRE(value3 == 0);
|
||||
|
||||
valueChanged(63);
|
||||
REQUIRE(value1 == 63);
|
||||
REQUIRE(value2 == 63);
|
||||
REQUIRE(value3 == 63);
|
||||
|
||||
valueChanged(101);
|
||||
REQUIRE(value1 == 101);
|
||||
REQUIRE(value2 == 63); // disconnected in slot.
|
||||
REQUIRE(value3 == 101);
|
||||
}
|
||||
|
||||
TEST_CASE("Disconnects OK if signal dead first", "[signal]")
|
||||
{
|
||||
connection conn2;
|
||||
{
|
||||
scoped_connection conn1;
|
||||
{
|
||||
signal<void(int)> valueChanged;
|
||||
conn2 = valueChanged.connect([](int) {
|
||||
});
|
||||
// Just unused.
|
||||
valueChanged.connect([](int) {
|
||||
});
|
||||
conn1 = valueChanged.connect([](int) {
|
||||
});
|
||||
}
|
||||
REQUIRE(conn2.connected());
|
||||
REQUIRE(conn1.connected());
|
||||
conn2.disconnect();
|
||||
REQUIRE(!conn2.connected());
|
||||
REQUIRE(conn1.connected());
|
||||
}
|
||||
conn2.disconnect();
|
||||
}
|
||||
|
||||
TEST_CASE("Returns last called slot result with default combiner", "[signal]")
|
||||
{
|
||||
connection conn2;
|
||||
{
|
||||
scoped_connection conn1;
|
||||
{
|
||||
signal<int(int)> absSignal;
|
||||
conn2 = absSignal.connect([](int value) {
|
||||
return value * value;
|
||||
});
|
||||
conn1 = absSignal.connect([](int value) {
|
||||
return abs(value);
|
||||
});
|
||||
absSignal(-1);
|
||||
|
||||
REQUIRE(absSignal(45) == 45);
|
||||
REQUIRE(absSignal(-1) == 1);
|
||||
REQUIRE(absSignal(-177) == 177);
|
||||
REQUIRE(absSignal(0) == 0);
|
||||
}
|
||||
REQUIRE(conn2.connected());
|
||||
conn2.disconnect();
|
||||
REQUIRE(!conn2.connected());
|
||||
}
|
||||
conn2.disconnect();
|
||||
REQUIRE(!conn2.connected());
|
||||
}
|
||||
|
||||
TEST_CASE("Works with custom any_of combiner", "[signal]")
|
||||
{
|
||||
using cancellable_signal = signal<bool(std::string), any_of_combiner>;
|
||||
cancellable_signal startRequested;
|
||||
auto conn1 = startRequested.connect([](std::string op) {
|
||||
return op == "1";
|
||||
});
|
||||
auto conn2 = startRequested.connect([](std::string op) {
|
||||
return op == "1" || op == "2";
|
||||
});
|
||||
REQUIRE(startRequested("0") == false);
|
||||
REQUIRE(startRequested("1") == true);
|
||||
REQUIRE(startRequested("2") == true);
|
||||
REQUIRE(startRequested("3") == false);
|
||||
conn1.disconnect();
|
||||
conn2.disconnect();
|
||||
REQUIRE(startRequested("0") == false);
|
||||
REQUIRE(startRequested("1") == false);
|
||||
REQUIRE(startRequested("2") == false);
|
||||
REQUIRE(startRequested("3") == false);
|
||||
}
|
||||
|
||||
TEST_CASE("Can release scoped connection", "[signal]")
|
||||
{
|
||||
int value2 = 0;
|
||||
int value3 = 0;
|
||||
signal<void(int)> valueChanged;
|
||||
connection conn1;
|
||||
{
|
||||
scoped_connection conn2;
|
||||
scoped_connection conn3;
|
||||
conn2 = valueChanged.connect([&value2](int x) {
|
||||
value2 = x;
|
||||
});
|
||||
conn3 = valueChanged.connect([&value3](int x) {
|
||||
value3 = x;
|
||||
});
|
||||
|
||||
valueChanged(42);
|
||||
REQUIRE(value2 == 42);
|
||||
REQUIRE(value3 == 42);
|
||||
REQUIRE(conn2.connected());
|
||||
REQUIRE(conn3.connected());
|
||||
REQUIRE(!conn1.connected());
|
||||
|
||||
conn1 = conn3.release();
|
||||
REQUIRE(conn2.connected());
|
||||
REQUIRE(!conn3.connected());
|
||||
REQUIRE(conn1.connected());
|
||||
valueChanged(144);
|
||||
REQUIRE(value2 == 144);
|
||||
REQUIRE(value3 == 144);
|
||||
}
|
||||
|
||||
// conn2 disconnected, conn1 connected.
|
||||
valueChanged(17);
|
||||
REQUIRE(value2 == 144);
|
||||
REQUIRE(value3 == 17);
|
||||
REQUIRE(conn1.connected());
|
||||
|
||||
conn1.disconnect();
|
||||
valueChanged(90);
|
||||
REQUIRE(value2 == 144);
|
||||
REQUIRE(value3 == 17);
|
||||
}
|
||||
|
||||
TEST_CASE("Can use signal with more than one argument", "[signal]")
|
||||
{
|
||||
signal<void(int, std::string, std::vector<std::string>)> event;
|
||||
|
||||
int value1 = 0;
|
||||
std::string value2;
|
||||
std::vector<std::string> value3;
|
||||
event.connect([&](int v1, const std::string& v2, const std::vector<std::string>& v3) {
|
||||
value1 = v1;
|
||||
value2 = v2;
|
||||
value3 = v3;
|
||||
});
|
||||
|
||||
event(9815, "using namespace std::literals!"s, std::vector{ "std::vector"s, "using namespace std::literals"s });
|
||||
REQUIRE(value1 == 9815);
|
||||
REQUIRE(value2 == "using namespace std::literals!"s);
|
||||
REQUIRE(value3 == std::vector{ "std::vector"s, "using namespace std::literals"s });
|
||||
}
|
||||
|
||||
TEST_CASE("Can blocks slots using shared_connection_block", "[signal]")
|
||||
{
|
||||
bool callbackShouldBeCalled = true;
|
||||
bool callbackCalled = false;
|
||||
const int value = 123;
|
||||
signal<void(int)> event;
|
||||
auto conn = event.connect([&](int gotValue) {
|
||||
CHECK(gotValue == value);
|
||||
callbackCalled = true;
|
||||
if (!callbackShouldBeCalled)
|
||||
{
|
||||
FAIL("callback is blocked and should not be called");
|
||||
}
|
||||
},
|
||||
advanced_tag{});
|
||||
event(value);
|
||||
REQUIRE(callbackCalled);
|
||||
shared_connection_block block(conn);
|
||||
callbackShouldBeCalled = false;
|
||||
callbackCalled = false;
|
||||
event(value);
|
||||
REQUIRE(!callbackCalled);
|
||||
block.unblock();
|
||||
callbackShouldBeCalled = true;
|
||||
event(value);
|
||||
REQUIRE(callbackCalled);
|
||||
}
|
||||
|
||||
TEST_CASE("Other slots are unaffected by the block", "[signal]")
|
||||
{
|
||||
bool callback1Called = false;
|
||||
bool callback2Called = false;
|
||||
const int value = 123;
|
||||
signal<void(int)> event;
|
||||
auto conn1 = event.connect([&](int gotValue) {
|
||||
CHECK(gotValue == value);
|
||||
callback1Called = true;
|
||||
},
|
||||
advanced_tag{});
|
||||
auto conn2 = event.connect([&](int) {
|
||||
callback2Called = true;
|
||||
FAIL("callback is blocked and should not be called");
|
||||
},
|
||||
advanced_tag{});
|
||||
shared_connection_block block(conn2);
|
||||
event(value);
|
||||
REQUIRE(callback1Called);
|
||||
REQUIRE(!callback2Called);
|
||||
}
|
||||
|
||||
TEST_CASE("Multiple blocks block until last one is unblocked", "[signal]")
|
||||
{
|
||||
bool callbackShouldBeCalled = false;
|
||||
bool callbackCalled = false;
|
||||
const int value = 123;
|
||||
signal<void(int)> event;
|
||||
auto conn = event.connect([&](int gotValue) {
|
||||
CHECK(gotValue == value);
|
||||
callbackCalled = true;
|
||||
if (!callbackShouldBeCalled)
|
||||
{
|
||||
FAIL("callback is blocked and should not be called");
|
||||
}
|
||||
},
|
||||
advanced_tag{});
|
||||
shared_connection_block block1(conn);
|
||||
shared_connection_block block2(conn);
|
||||
event(value);
|
||||
REQUIRE(!callbackCalled);
|
||||
block1.unblock();
|
||||
event(value);
|
||||
REQUIRE(!callbackCalled);
|
||||
block1.block();
|
||||
block2.unblock();
|
||||
event(value);
|
||||
REQUIRE(!callbackCalled);
|
||||
block1.unblock();
|
||||
callbackShouldBeCalled = true;
|
||||
event(value);
|
||||
REQUIRE(callbackCalled);
|
||||
}
|
||||
|
||||
TEST_CASE("Can copy and move shared_connection_block objects", "[signal]")
|
||||
{
|
||||
bool callbackShouldBeCalled = false;
|
||||
bool callbackCalled = false;
|
||||
const int value = 123;
|
||||
signal<void(int)> event;
|
||||
auto conn = event.connect([&](int gotValue) {
|
||||
CHECK(gotValue == value);
|
||||
callbackCalled = true;
|
||||
if (!callbackShouldBeCalled)
|
||||
{
|
||||
FAIL("callback is blocked and should not be called");
|
||||
}
|
||||
},
|
||||
advanced_tag{});
|
||||
shared_connection_block block1(conn);
|
||||
|
||||
shared_connection_block block2(block1);
|
||||
event(value);
|
||||
REQUIRE(block1.blocking());
|
||||
REQUIRE(block2.blocking());
|
||||
REQUIRE(!callbackCalled);
|
||||
|
||||
shared_connection_block block3(std::move(block2));
|
||||
event(value);
|
||||
REQUIRE(block1.blocking());
|
||||
REQUIRE(!block2.blocking());
|
||||
REQUIRE(block3.blocking());
|
||||
REQUIRE(!callbackCalled);
|
||||
|
||||
block2 = block3;
|
||||
event(value);
|
||||
REQUIRE(block1.blocking());
|
||||
REQUIRE(block2.blocking());
|
||||
REQUIRE(block3.blocking());
|
||||
REQUIRE(!callbackCalled);
|
||||
|
||||
block3 = std::move(block2);
|
||||
event(value);
|
||||
REQUIRE(block1.blocking());
|
||||
REQUIRE(!block2.blocking());
|
||||
REQUIRE(block3.blocking());
|
||||
REQUIRE(!callbackCalled);
|
||||
|
||||
block3 = shared_connection_block(conn, false);
|
||||
event(value);
|
||||
REQUIRE(block1.blocking());
|
||||
REQUIRE(!block2.blocking());
|
||||
REQUIRE(!block3.blocking());
|
||||
REQUIRE(!callbackCalled);
|
||||
|
||||
block1.unblock();
|
||||
callbackShouldBeCalled = true;
|
||||
event(value);
|
||||
REQUIRE(!block1.blocking());
|
||||
REQUIRE(!block2.blocking());
|
||||
REQUIRE(!block3.blocking());
|
||||
REQUIRE(callbackCalled);
|
||||
}
|
||||
|
||||
TEST_CASE("Unblocks when shared_connection_block goes out of scope")
|
||||
{
|
||||
bool callbackCalled = false;
|
||||
const int value = 123;
|
||||
signal<void(int)> event;
|
||||
auto conn = event.connect([&](int gotValue) {
|
||||
CHECK(gotValue == value);
|
||||
callbackCalled = true;
|
||||
},
|
||||
advanced_tag{});
|
||||
|
||||
callbackCalled = false;
|
||||
event(value);
|
||||
CHECK(callbackCalled);
|
||||
|
||||
{
|
||||
callbackCalled = false;
|
||||
shared_connection_block block(conn);
|
||||
event(value);
|
||||
CHECK(!callbackCalled);
|
||||
|
||||
{
|
||||
callbackCalled = false;
|
||||
shared_connection_block block2(conn);
|
||||
event(value);
|
||||
CHECK(!callbackCalled);
|
||||
}
|
||||
}
|
||||
|
||||
callbackCalled = false;
|
||||
event(value);
|
||||
CHECK(callbackCalled);
|
||||
}
|
||||
|
||||
TEST_CASE("Can disconnect advanced slot using advanced_scoped_connection", "[signal]")
|
||||
{
|
||||
signal<void(int)> valueChanged;
|
||||
|
||||
int value1 = 0;
|
||||
int value2 = 0;
|
||||
int value3 = 0;
|
||||
{
|
||||
advanced_scoped_connection conn1 = valueChanged.connect([&value1](int value) {
|
||||
value1 = value;
|
||||
},
|
||||
advanced_tag{});
|
||||
{
|
||||
advanced_scoped_connection conn2 = valueChanged.connect([&value2](int value) {
|
||||
value2 = value;
|
||||
},
|
||||
advanced_tag{});
|
||||
valueChanged.connect([&value3](int value) {
|
||||
value3 = value;
|
||||
});
|
||||
REQUIRE(value1 == 0);
|
||||
REQUIRE(value2 == 0);
|
||||
REQUIRE(value3 == 0);
|
||||
|
||||
valueChanged(10);
|
||||
REQUIRE(value1 == 10);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == 10);
|
||||
}
|
||||
|
||||
// conn2 disconnected.
|
||||
valueChanged(-99);
|
||||
REQUIRE(value1 == -99);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == -99);
|
||||
}
|
||||
|
||||
// conn1 disconnected.
|
||||
valueChanged(17);
|
||||
REQUIRE(value1 == -99);
|
||||
REQUIRE(value2 == 10);
|
||||
REQUIRE(value3 == 17);
|
||||
}
|
||||
|
||||
TEST_CASE("Can move signal", "[signal]")
|
||||
{
|
||||
signal<void()> src;
|
||||
|
||||
int srcFireCount = 0;
|
||||
auto srcConn = src.connect([&srcFireCount] {
|
||||
++srcFireCount;
|
||||
});
|
||||
|
||||
src();
|
||||
REQUIRE(srcFireCount == 1);
|
||||
|
||||
auto dst = std::move(src);
|
||||
|
||||
int dstFireCount = 0;
|
||||
auto dstConn = dst.connect([&dstFireCount] {
|
||||
++dstFireCount;
|
||||
});
|
||||
|
||||
dst();
|
||||
REQUIRE(srcFireCount == 2);
|
||||
REQUIRE(dstFireCount == 1);
|
||||
|
||||
srcConn.disconnect();
|
||||
dstConn.disconnect();
|
||||
dst();
|
||||
REQUIRE(srcFireCount == 2);
|
||||
REQUIRE(dstFireCount == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Can swap signals", "[signal]")
|
||||
{
|
||||
signal<void()> s1;
|
||||
signal<void()> s2;
|
||||
|
||||
int s1FireCount = 0;
|
||||
int s2FireCount = 0;
|
||||
|
||||
s1.connect([&s1FireCount] {
|
||||
++s1FireCount;
|
||||
});
|
||||
|
||||
s2.connect([&s2FireCount] {
|
||||
++s2FireCount;
|
||||
});
|
||||
|
||||
std::swap(s1, s2);
|
||||
|
||||
s1();
|
||||
REQUIRE(s1FireCount == 0);
|
||||
REQUIRE(s2FireCount == 1);
|
||||
|
||||
s2();
|
||||
REQUIRE(s1FireCount == 1);
|
||||
REQUIRE(s2FireCount == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Signal can be destroyed inside its slot and will call the rest of its slots", "[signal]")
|
||||
{
|
||||
std::optional<signal<void()>> s;
|
||||
s.emplace();
|
||||
s->connect([&] {
|
||||
s.reset();
|
||||
});
|
||||
bool called = false;
|
||||
s->connect([&] {
|
||||
called = true;
|
||||
});
|
||||
(*s)();
|
||||
CHECK(called);
|
||||
}
|
||||
|
||||
TEST_CASE("Signal can be used as a slot for another signal", "[signal]")
|
||||
{
|
||||
signal<void()> s1;
|
||||
bool called = false;
|
||||
s1.connect([&] {
|
||||
called = true;
|
||||
});
|
||||
|
||||
signal<void()> s2;
|
||||
s2.connect(s1);
|
||||
|
||||
s2();
|
||||
|
||||
CHECK(called);
|
||||
}
|
||||
|
||||
// memory leak fix
|
||||
TEST_CASE("Releases lambda and its captured const data", "[signal]")
|
||||
{
|
||||
struct Captured
|
||||
{
|
||||
Captured(bool& released)
|
||||
: m_released(released)
|
||||
{
|
||||
}
|
||||
|
||||
~Captured()
|
||||
{
|
||||
m_released = true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool& m_released;
|
||||
};
|
||||
|
||||
bool released = false;
|
||||
|
||||
{
|
||||
const auto captured = std::make_shared<Captured>(released);
|
||||
|
||||
signal<void()> changeSignal;
|
||||
changeSignal.connect([captured]{});
|
||||
}
|
||||
|
||||
CHECK(released);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user