Merge remote-tracking branch 'upstream/HEAD'
92
.github/ISSUE_TEMPLATE/1-PROBLEM_REPORT.yml
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
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: 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: 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: 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
|
||||
73
.github/ISSUE_TEMPLATE/PROBLEM_REPORT.yml
vendored
@@ -1,73 +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: 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
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Weekly Build
|
||||
name: Build Release
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
schedule:
|
||||
- cron: "0 0 * * 3"
|
||||
workflow_dispatch:
|
||||
@@ -8,10 +10,10 @@ permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
tag_build:
|
||||
upload_src:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
build_tag: ${{ steps.tag_build.outputs.build_tag }}
|
||||
build_tag: ${{ steps.get_tag.outputs.build_tag }}
|
||||
steps:
|
||||
- name: Harden the runner (Audit all outbound calls)
|
||||
uses: step-security/harden-runner@95d9a5deda9de15063e7595e9719c11c38c90ae2 # v2.13.2
|
||||
@@ -21,20 +23,25 @@ jobs:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 2
|
||||
fetch-tags: true
|
||||
submodules: 'recursive'
|
||||
|
||||
- name: Tag Build
|
||||
id: tag_build
|
||||
- name: get tag and create release if weekly
|
||||
id: get_tag
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
export BUILD_TAG=weekly-$(date "+%Y.%m.%d")
|
||||
if [ "${{ github.event_name }}" = "release" ]; then
|
||||
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 || true
|
||||
fi
|
||||
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
|
||||
echo "build_tag=${BUILD_TAG}" >> "$GITHUB_OUTPUT"
|
||||
gh release create ${BUILD_TAG} --title "Development Build ${BUILD_TAG}" -F .github/workflows/weekly-build-notes.md --prerelease || true
|
||||
|
||||
- name: Upload Source
|
||||
id: upload_source
|
||||
@@ -42,10 +49,10 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
python3 package/rattler-build/scripts/make_version_file.py ../freecad_version.txt
|
||||
python3 package/scripts/write_version_info.py ../freecad_version.txt
|
||||
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
|
||||
git config user.name 'github-actions[bot]'
|
||||
git apply package/rattler-build/scripts/disable_git_info.patch
|
||||
git apply package/disable_git_info.patch
|
||||
git commit -a -m "Disable git info write to Version.h"
|
||||
git archive HEAD -o freecad_source_${BUILD_TAG}.tar
|
||||
git submodule foreach --recursive \
|
||||
@@ -57,13 +64,13 @@ jobs:
|
||||
gh release upload --clobber ${BUILD_TAG} "freecad_source_${BUILD_TAG}.tar.gz" "freecad_source_${BUILD_TAG}.tar.gz-SHA256.txt"
|
||||
|
||||
build:
|
||||
needs: tag_build
|
||||
needs: upload_src
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- { target: linux-64, os: ubuntu-22.04 }
|
||||
- { target: linux-arm64, os: ubuntu-22.04-arm }
|
||||
- { target: osx-64, os: macos-13 }
|
||||
- { target: osx-64, os: macos-15-intel }
|
||||
- { target: osx-arm64, os: macos-latest }
|
||||
- { target: win-64, os: windows-latest }
|
||||
fail-fast: false
|
||||
@@ -88,10 +95,8 @@ jobs:
|
||||
- name: Set Platform Environment Variables
|
||||
shell: bash -l {0}
|
||||
env:
|
||||
BUILD_TAG: ${{ needs.tag_build.outputs.build_tag }}
|
||||
OPERATING_SYSTEM: ${{ runner.os }}
|
||||
run: |
|
||||
echo "BUILD_TAG=${BUILD_TAG}" >> "$GITHUB_ENV"
|
||||
if [[ $OPERATING_SYSTEM == 'Windows' ]]; then
|
||||
echo 'PIXI_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
|
||||
echo 'RATTLER_CACHE_DIR=D:\rattler' >> "$GITHUB_ENV"
|
||||
@@ -100,6 +105,7 @@ jobs:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
ref: ${{ github.sha }}
|
||||
fetch-depth: 2
|
||||
fetch-tags: true
|
||||
submodules: 'recursive'
|
||||
@@ -110,6 +116,7 @@ jobs:
|
||||
cache: false
|
||||
|
||||
- name: Install the Apple certificate and provisioning profile
|
||||
id: get_cert
|
||||
if: runner.os == 'macOS'
|
||||
env:
|
||||
APP_SPECIFIC_PASSWORD: ${{ secrets.APP_SPECIFIC_PASSWORD }}
|
||||
@@ -120,9 +127,15 @@ jobs:
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||
run: |
|
||||
if [ -z "$BUILD_CERTIFICATE_BASE64" ]; then
|
||||
echo "has_cert=false" >> $GITHUB_OUTPUT
|
||||
echo "No certificate avalable... skipping" && exit 0
|
||||
else
|
||||
echo "has_cert=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
# create variables
|
||||
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
||||
PP_PATH=$RUNNER_TEMP/FreeCAD_Weekly.provisionprofile
|
||||
PP_PATH=$RUNNER_TEMP/FreeCAD_bundle.provisionprofile
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
|
||||
export KEYCHAIN_PASSWORD=$(openssl rand -base64 8)
|
||||
@@ -152,12 +165,13 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
|
||||
SIGN_RELEASE: "true"
|
||||
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: |
|
||||
python3 package/rattler-build/scripts/make_version_file.py ../freecad_version.txt
|
||||
git apply package/rattler-build/scripts/disable_git_info.patch
|
||||
python3 package/scripts/write_version_info.py ../freecad_version.txt
|
||||
cd package/rattler-build
|
||||
pixi install
|
||||
pixi run -e package create_bundle
|
||||
2
.github/workflows/dependency-review.yml
vendored
@@ -24,4 +24,4 @@ jobs:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@56339e523c0409420f6c2c9a2f4292bbb3c07dd3 # v4
|
||||
uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4
|
||||
|
||||
11
.github/workflows/fedora-daily.yml
vendored
@@ -8,7 +8,10 @@ on:
|
||||
|
||||
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
|
||||
@@ -16,7 +19,13 @@ jobs:
|
||||
- name: setup copr token
|
||||
run: |
|
||||
mkdir -p ~/.config
|
||||
echo "$copr_token" > ~/.config/copr
|
||||
echo \
|
||||
"[copr-cli]
|
||||
login = $copr_login
|
||||
username = $copr_username
|
||||
token = $copr_token
|
||||
copr_url = https://copr.fedorainfracloud.org
|
||||
" > ~/.config/copr
|
||||
- name: checkout sources
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
@@ -13,6 +13,7 @@ jobs:
|
||||
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
|
||||
@@ -45,8 +46,8 @@ jobs:
|
||||
|
||||
- name: Commit changes
|
||||
run: |
|
||||
git config --global user.name "github-actions"
|
||||
git config --global user.email "github-actions@github.com"
|
||||
git config --global user.name "freecad-gh-actions-translation-bot"
|
||||
git config --global user.email "freecad-gh-actions-translation-bot@github.com"
|
||||
git add src
|
||||
git commit -m "Update translations from Crowdin" || echo "No changes to commit"
|
||||
|
||||
@@ -56,12 +57,11 @@ jobs:
|
||||
git push origin update-crowdin-translations --force
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
|
||||
uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412
|
||||
with:
|
||||
branch: update-crowdin-translations
|
||||
title: "Update translations from Crowdin"
|
||||
body: "Automatic Crowdin update."
|
||||
labels: "translations, automated"
|
||||
token: ${{ secrets.GH_TOKEN_FOR_CROWDIN_SYNC }}
|
||||
delete-branch: true
|
||||
add-paths: |
|
||||
|
||||
2
.github/workflows/issue-metrics.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
echo "last_month=$first_day..$last_day" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Run issue-metrics tool
|
||||
uses: github/issue-metrics@637a24e71b78bc10881e61972b19ea9ff736e14a # v3.25.2
|
||||
uses: github/issue-metrics@78b1d469a1b1c94945b15bd71dedcb1928667f49 # v3.25.3
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SEARCH_QUERY: 'repo:FreeCAD/FreeCAD is:issue created:${{ env.last_month }}'
|
||||
|
||||
2
.github/workflows/sub_wrapup.yml
vendored
@@ -62,7 +62,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p ${{ env.artifactsDownloadDir }}
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
with:
|
||||
path: ${{ env.artifactsDownloadDir }}
|
||||
- name: Save input data to file
|
||||
|
||||
@@ -3,7 +3,7 @@ files_to_sync:
|
||||
- .packit.yaml
|
||||
actions:
|
||||
post-upstream-clone:
|
||||
- bash -c 'BUILD_TAG=dev /usr/bin/python3 package/rattler-build/scripts/make_version_file.py freecad_version.txt'
|
||||
- bash -c '/usr/bin/python3 package/scripts/write_version_info.py freecad_version.txt'
|
||||
- rm -f freecad-sources.tar.gz
|
||||
changelog-entry:
|
||||
- bash -c 'git log --no-merges --pretty="format:- %s (%an)" $(git describe --tags --abbrev=0 )..HEAD -- |sed 's/%/%%/g''
|
||||
|
||||
@@ -82,15 +82,11 @@ persistent=yes
|
||||
|
||||
# Minimum Python version to use for version dependent checks. Will default to
|
||||
# the version used to run pylint.
|
||||
py-version=3.8
|
||||
py-version=3.10
|
||||
|
||||
# Discover python modules and packages in the file system subtree.
|
||||
recursive=no
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages.
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
@@ -71,10 +71,6 @@
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
},
|
||||
"BUILD_REVERSEENGINEERING": {
|
||||
"type": "BOOL",
|
||||
"value": "OFF"
|
||||
},
|
||||
"ENABLE_DEVELOPER_TESTS": {
|
||||
"type": "BOOL",
|
||||
"value": "ON"
|
||||
|
||||
5
package/WindowsInstaller/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
*.exe
|
||||
*-SHA256.txt
|
||||
MSVCRedist
|
||||
FreeCAD
|
||||
version.nsh
|
||||
47
package/WindowsInstaller/Delete.bat
Normal file
@@ -0,0 +1,47 @@
|
||||
del /S *_d.*
|
||||
del /S *_debug.*
|
||||
del /S *.pyc
|
||||
del /S *.pdb
|
||||
del /S boost*-gd-*.dll
|
||||
cd bin
|
||||
del assistant.exe
|
||||
del Coin4d.dll
|
||||
del designer.exe
|
||||
del freetyped.dll
|
||||
del libcrypto-3d.dll
|
||||
del libEGLd.dll
|
||||
del libGLESv2d.dll
|
||||
del libssl-3d.dll
|
||||
del linguist.exe
|
||||
del qdoc.exe
|
||||
del Qt6Concurrentd.dll
|
||||
del Qt6Cored.dll
|
||||
del Qt6DBusd.dll
|
||||
del Qt6DesignerComponentsd.dll
|
||||
del Qt6Designerd.dll
|
||||
del Qt6Guid.dll
|
||||
del Qt6Helpd.dll
|
||||
del Qt6LabsAnimationd.dll
|
||||
del Qt6LabsFolderListModeld.dll
|
||||
del Qt6LabsPlatformd.dll
|
||||
del Qt6LabsSettingsd.dll
|
||||
del Qt6LabsSharedImaged.dll
|
||||
del Qt6LabsWavefrontMeshd.dll
|
||||
del Qt6MultimediaWidgetsd.dll
|
||||
del Qt6Multimediad.dll
|
||||
del Qt6Networkd.dll
|
||||
del Qt6OpenGLWidgetsd.dll
|
||||
del Qt6OpenGLd.dll
|
||||
del Qt6PrintSupportd.dll
|
||||
del Qt6SpatialAudiod.dll
|
||||
del Qt6Sqld.dll
|
||||
del Qt6SvgWidgetsd.dll
|
||||
del Qt6Svgd.dll
|
||||
del Qt6Testd.dll
|
||||
del Qt6UiToolsd.dll
|
||||
del Qt6Widgetsd.dll
|
||||
del Qt6Xmld.dll
|
||||
del QtWebEngineProcessd.exe
|
||||
del Quarter1d.dll
|
||||
del xerces-c_3_2D.dll
|
||||
del zlibd.dll
|
||||
62
package/WindowsInstaller/Settings.nsh
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
|
||||
Settings for FreeCAD installer
|
||||
|
||||
These typically need to be modified for each FreeCAD release
|
||||
|
||||
*/
|
||||
|
||||
# Make the installer as small as possible
|
||||
# Using /SOLID is usually better for file size but it can't be used if the original size is
|
||||
# more than 2GB, if building with /SOLID fails try disabling it
|
||||
# comment this or use /DFC_TEST_BUILD command line option for testing builds since it will reduce
|
||||
# the time to create an installer a lot at the cost of a much greater file size.
|
||||
# So assure it is active for release builds!
|
||||
!ifndef FC_TEST_BUILD
|
||||
SetCompressor /SOLID lzma
|
||||
!endif
|
||||
|
||||
#--------------------------------
|
||||
# File locations
|
||||
# !!! you may need to adjust them to the folders in your Windows system !!!
|
||||
# can be specified with /D command line argument to makensis.exe
|
||||
!ifndef FILES_FREECAD
|
||||
!define FILES_FREECAD "${__FILEDIR__}\FreeCAD"
|
||||
!endif
|
||||
!ifndef FILES_THUMBS
|
||||
!define FILES_THUMBS "${__FILEDIR__}\thumbnail"
|
||||
!endif
|
||||
|
||||
# msvc redistributables location is required for LibPack builds but not conda
|
||||
# when using a LibPack build set the redistributables directory location here
|
||||
# or with /D command line argument to makensis.exe
|
||||
#!define FILES_DEPS "${__FILEDIR__}\MSVCRedist"
|
||||
|
||||
#--------------------------------
|
||||
# get version info from freecadcmd
|
||||
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"import datetime; print(f'!define COPYRIGHT_YEAR {datetime.date.today().year}')$\">${__FILEDIR__}\version.nsh" = 0
|
||||
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_MAJOR \$\"{App.Version()[0]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
|
||||
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_MINOR \$\"{App.Version()[1]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
|
||||
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_PATCH \$\"{App.Version()[2]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
|
||||
!system "${FILES_FREECAD}\bin\freecadcmd.exe --safe-mode -c $\"print(f'!define APP_VERSION_REVISION \$\"{App.Version()[3].split()[0]}\$\"')$\">>${__FILEDIR__}\version.nsh" = 0
|
||||
!include "${__FILEDIR__}\version.nsh"
|
||||
!delfile "${__FILEDIR__}\version.nsh"
|
||||
|
||||
!define APP_VERSION_EMERGENCY "" # use "1" for an emergency release of FreeCAD otherwise ""
|
||||
# alternatively you can use APP_VERSION_EMERGENCY for a custom suffix of the version number
|
||||
!define APP_EMERGENCY_DOT "" # use "." for an emergency release of FreeCAD otherwise ""
|
||||
!define APP_VERSION_BUILD 1 # Start with 1 for the installer releases of each version
|
||||
|
||||
!define APP_VERSION "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}${APP_EMERGENCY_DOT}${APP_VERSION_EMERGENCY}" # Version to display
|
||||
|
||||
#--------------------------------
|
||||
# Installer file name
|
||||
# Typical names for the release are "FreeCAD-020-Installer-1.exe" etc.
|
||||
|
||||
!ifndef ExeFile
|
||||
!define ExeFile "${APP_NAME}_${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}${APP_VERSION_EMERGENCY}-Windows-x86_64-installer-${APP_VERSION_BUILD}.exe"
|
||||
!endif
|
||||
|
||||
#--------------------------------
|
||||
# installer bit type - FreeCAD is only provided as 64bit build
|
||||
!define MULTIUSER_USE_PROGRAMFILES64
|
||||
@@ -1 +1,2 @@
|
||||
signtool.exe sign /f FCweborg.pfx /p FreeCADIsCool /fd sha512 /tr http://timestamp.digicert.com /td sha512 /v %1
|
||||
certutil -hashfile %1 SHA256 > %1-SHA256.txt
|
||||
BIN
package/WindowsInstaller/graphics/banner.bmp
Normal file
|
After Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
BIN
package/WindowsInstaller/graphics/header.bmp
Normal file
|
After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 91 KiB After Width: | Height: | Size: 91 KiB |
@@ -13,13 +13,13 @@ Configuration and variables of FreeCAD installer
|
||||
# Names and version
|
||||
|
||||
!define APP_NAME "FreeCAD"
|
||||
!define APP_VERSION_NUMBER "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_REVISION}.${APP_VERSION_BUILD}"
|
||||
!define APP_VERSION_NUMBER "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}.${APP_VERSION_BUILD}"
|
||||
# For the proposed install folder we use the scheme "FreeCAD 0.18"
|
||||
# however for the Registry, we need the scheme "FreeCAD 0.18.x" in order
|
||||
# to check if it is exactly this version (to support side-by-side installations)
|
||||
!define APP_SERIES_NAME "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}"
|
||||
!define APP_SERIES_KEY "${APP_VERSION_MAJOR}${APP_VERSION_MINOR}${APP_VERSION_REVISION}${APP_VERSION_EMERGENCY}"
|
||||
!define APP_SERIES_KEY2 "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_REVISION}${APP_EMERGENCY_DOT}${APP_VERSION_EMERGENCY}"
|
||||
!define APP_SERIES_KEY "${APP_VERSION_MAJOR}${APP_VERSION_MINOR}${APP_VERSION_PATCH}${APP_VERSION_EMERGENCY}"
|
||||
!define APP_SERIES_KEY2 "${APP_VERSION_MAJOR}.${APP_VERSION_MINOR}.${APP_VERSION_PATCH}${APP_EMERGENCY_DOT}${APP_VERSION_EMERGENCY}"
|
||||
!define APP_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\${APP_NAME}.exe"
|
||||
!define APP_DIR "${APP_NAME} ${APP_SERIES_NAME}"
|
||||
# Fixme: FC should use different preferences folder for every release
|
||||
@@ -60,7 +60,7 @@ Configuration and variables of FreeCAD installer
|
||||
|
||||
!define SETUP_ICON "icons\FreeCAD.ico"
|
||||
!define SETUP_HEADERIMAGE "graphics\header.bmp"
|
||||
!define SETUP_WIZARDIMAGE "graphics\orange.bmp"
|
||||
!define SETUP_WIZARDIMAGE "graphics\banner.bmp"
|
||||
!define SETUP_UNINSTALLER "Uninstall-${APP_NAME}.exe"
|
||||
!define SETUP_UNINSTALLER_KEY "${APP_NAME}${APP_SERIES_KEY}"
|
||||
|
||||
@@ -92,7 +92,7 @@ BrandingText " "
|
||||
|
||||
VIProductVersion "${APP_VERSION_NUMBER}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "${APP_NAME}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "${APP_DIR}.${APP_VERSION_REVISION}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductVersion" "${APP_DIR}.${APP_VERSION_PATCH}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "${APP_INFO}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${APP_VERSION}"
|
||||
VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "${APP_COPYRIGHT}"
|
||||
@@ -50,7 +50,7 @@ Function PostMultiUserPageInit
|
||||
|
||||
# check if there is an existing FreeCAD installation of the same FreeCAD series
|
||||
# we usually don't release more than 10 versions so with 20 we are safe to check if a newer version is installed
|
||||
IntOp $4 ${APP_VERSION_REVISION} + 20
|
||||
IntOp $4 ${APP_VERSION_PATCH} + 20
|
||||
${for} $5 0 $4
|
||||
ReadRegStr $0 SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}${APP_VERSION_MAJOR}${APP_VERSION_MINOR}$5" "DisplayVersion"
|
||||
# also check for an emergency release
|
||||
@@ -110,7 +110,8 @@ Function .onInit
|
||||
${orif} $R0 == "5.1" # XP
|
||||
${orif} $R0 == "5.2" # 2003
|
||||
${orif} $R0 == "6.0" # Vista
|
||||
MessageBox MB_OK|MB_ICONSTOP "${APP_NAME} ${APP_VERSION} requires Windows 7 or newer." /SD IDOK
|
||||
${orif} $R0 == "6.1" # 7
|
||||
MessageBox MB_OK|MB_ICONSTOP "${APP_NAME} ${APP_VERSION} requires Windows 8 or newer." /SD IDOK
|
||||
Quit
|
||||
${endif}
|
||||
|
||||
@@ -130,7 +131,7 @@ Function .onInit
|
||||
# plugin must be unloaded
|
||||
${nsProcess::Unload}
|
||||
|
||||
# initialize the multi-uder installer UI
|
||||
# initialize the multi-user installer UI
|
||||
!insertmacro MULTIUSER_INIT
|
||||
|
||||
# this can be reset to "true" in section SecDesktop
|
||||
@@ -18,8 +18,8 @@
|
||||
# and returns the number of the character in the FindStr where the SearchStr was found (Pointer)
|
||||
# if nothing was found or the search is impossible the Pointer is set to -1
|
||||
|
||||
StrLen $R2 ${SearchStr}
|
||||
StrLen $R4 ${FindStr}
|
||||
StrLen $R2 "${SearchStr}"
|
||||
StrLen $R4 "${FindStr}"
|
||||
StrCpy $R5 0
|
||||
${if} $R2 == 0
|
||||
${orif} $R4 == 0
|
||||
@@ -27,8 +27,8 @@
|
||||
${endif}
|
||||
IntCmp $R4 $R2 loopA NotFound
|
||||
loopA:
|
||||
StrCpy $R3 ${FindStr} $R2 $R5
|
||||
StrCmp $R3 ${SearchStr} Found
|
||||
StrCpy $R3 "${FindStr}" $R2 $R5
|
||||
StrCmp $R3 "${SearchStr}" Found
|
||||
IntOp $R5 $R5 + 1
|
||||
IntCmp $R4 $R5 loopA NotFound
|
||||
Goto loopA
|
||||
@@ -7,7 +7,7 @@ Language: English
|
||||
|
||||
${LangFileString} TEXT_INSTALL_CURRENTUSER "(Installed for Current User)"
|
||||
|
||||
${LangFileString} TEXT_WELCOME "This wizard will guide you through the installation of $(^NameDA), $\r$\n\
|
||||
${LangFileString} TEXT_WELCOME "This wizard will guide you through the installation of $(^NameDA). $\r$\n\
|
||||
$\r$\n\
|
||||
$_CLICK"
|
||||
|
||||
@@ -8,14 +8,15 @@ Installation of program files, dictionaries and external components
|
||||
|
||||
#--------------------------------
|
||||
# Program files
|
||||
!include LogicLib.nsh
|
||||
|
||||
Section -ProgramFiles SecProgramFiles
|
||||
|
||||
# if the $INSTDIR does not contain "FreeCAD" we must add a subfolder to avoid that FreeCAD will e.g.
|
||||
# be installed directly to C:\programs - the uninstaller will then delete the whole
|
||||
# C:\programs directory
|
||||
StrCpy $String $INSTDIR
|
||||
StrCpy $Search ${APP_NAME}
|
||||
StrCpy $String "$INSTDIR"
|
||||
StrCpy $Search "${APP_NAME}"
|
||||
Call StrPoint # function from Utils.nsh
|
||||
${if} $Pointer == "-1"
|
||||
StrCpy $INSTDIR "$INSTDIR\${APP_DIR}"
|
||||
@@ -38,8 +39,11 @@ Section -ProgramFiles SecProgramFiles
|
||||
File /r "${FILES_FREECAD}\bin\*.*"
|
||||
|
||||
# MSVC redistributable DLLs
|
||||
SetOutPath "$INSTDIR\bin"
|
||||
File "${FILES_DEPS}\*.*"
|
||||
!ifdef FILES_DEPS
|
||||
!echo "Including MSVC Redist files from ${FILES_DEPS}"
|
||||
SetOutPath "$INSTDIR\bin"
|
||||
File "${FILES_DEPS}\*.*"
|
||||
!endif
|
||||
|
||||
# Others
|
||||
SetOutPath "$INSTDIR\data"
|
||||
@@ -52,10 +56,6 @@ Section -ProgramFiles SecProgramFiles
|
||||
File /r "${FILES_FREECAD}\lib\*.*"
|
||||
SetOutPath "$INSTDIR\Mod"
|
||||
File /r "${FILES_FREECAD}\Mod\*.*"
|
||||
SetOutPath "$INSTDIR\resources"
|
||||
File /r "${FILES_FREECAD}\resources\*.*"
|
||||
SetOutPath "$INSTDIR\translations"
|
||||
File /r "${FILES_FREECAD}\translations\*.*"
|
||||
SetOutPath "$INSTDIR"
|
||||
File /r "${FILES_THUMBS}"
|
||||
|
||||
12
package/rattler-build/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
linux/AppDir/usr
|
||||
linux/AppDir/*.desktop
|
||||
linux/AppDir/*.svg
|
||||
linux/AppDir/packages.txt
|
||||
windows/FreeCAD_*Windows*
|
||||
osx/FreeCAD.app
|
||||
**.AppImage
|
||||
**.AppImage.zsync
|
||||
**.dmg
|
||||
**.7z
|
||||
**.exe
|
||||
**-SHA256.txt
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
set -x
|
||||
|
||||
conda_env="AppDir/usr"
|
||||
|
||||
@@ -9,9 +9,6 @@ mkdir -p ${conda_env}
|
||||
|
||||
cp -a ../.pixi/envs/default/* ${conda_env}
|
||||
|
||||
export PATH="${PWD}/${conda_env}/bin:${PATH}"
|
||||
export CONDA_PREFIX="${PWD}/${conda_env}"
|
||||
|
||||
echo -e "\nDelete unnecessary stuff"
|
||||
rm -rf ${conda_env}/include
|
||||
find ${conda_env} -name \*.a -delete
|
||||
@@ -49,7 +46,7 @@ rm -rf ${conda_env}/lib/cmake/
|
||||
find . -name "*.h" -type f -delete
|
||||
find . -name "*.cmake" -type f -delete
|
||||
|
||||
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
|
||||
python_version=$(${conda_env}/bin/python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
|
||||
version_name="FreeCAD_${BUILD_TAG}-Linux-$(uname -m)-${python_version}"
|
||||
|
||||
echo -e "\################"
|
||||
@@ -62,6 +59,20 @@ sed -i "1s/.*/\nLIST OF PACKAGES:/" AppDir/packages.txt
|
||||
curl -LO https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage
|
||||
chmod a+x appimagetool-$(uname -m).AppImage
|
||||
|
||||
if [ "${UPLOAD_RELEASE}" == "true" ]; then
|
||||
case "${BUILD_TAG}" in
|
||||
*weekly*)
|
||||
GH_UPDATE_TAG="weeklies"
|
||||
;;
|
||||
*rc*)
|
||||
GH_UPDATE_TAG="${BUILD_TAG}"
|
||||
;;
|
||||
*)
|
||||
GH_UPDATE_TAG="latest"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
echo -e "\nCreate the appimage"
|
||||
# export GPG_TTY=$(tty)
|
||||
chmod a+x ./AppDir/AppRun
|
||||
@@ -69,7 +80,7 @@ chmod a+x ./AppDir/AppRun
|
||||
--comp zstd \
|
||||
--mksquashfs-opt -Xcompression-level \
|
||||
--mksquashfs-opt 22 \
|
||||
-u "gh-releases-zsync|FreeCAD|FreeCAD|${BUILD_TAG}|FreeCAD*$(uname -m)*.AppImage.zsync" \
|
||||
-u "gh-releases-zsync|FreeCAD|FreeCAD|${GH_UPDATE_TAG}|FreeCAD*$(uname -m)*.AppImage.zsync" \
|
||||
AppDir ${version_name}.AppImage
|
||||
# -s --sign-key ${GPG_KEY_ID} \
|
||||
|
||||
@@ -77,5 +88,13 @@ echo -e "\nCreate hash"
|
||||
sha256sum ${version_name}.AppImage > ${version_name}.AppImage-SHA256.txt
|
||||
|
||||
if [ "${UPLOAD_RELEASE}" == "true" ]; then
|
||||
gh release upload --clobber ${BUILD_TAG} "${version_name}.AppImage" "${version_name}.AppImage-SHA256.txt"
|
||||
gh release upload --clobber ${BUILD_TAG} "${version_name}.AppImage" "${version_name}.AppImage.zsync" "${version_name}.AppImage-SHA256.txt"
|
||||
if [ "${GH_UPDATE_TAG}" == "weeklies" ]; then
|
||||
generic_name="FreeCAD_weekly-Linux-$(uname -m)"
|
||||
mv "${version_name}.AppImage" "${generic_name}.AppImage"
|
||||
mv "${version_name}.AppImage.zsync" "${generic_name}.AppImage.zsync"
|
||||
mv "${version_name}.AppImage-SHA256.txt" "${generic_name}.AppImage-SHA256.txt"
|
||||
gh release create weeklies --prerelease | true
|
||||
gh release upload --clobber weeklies "${generic_name}.AppImage" "${generic_name}.AppImage.zsync" "${generic_name}.AppImage-SHA256.txt"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -9,9 +9,6 @@ mkdir -p ${conda_env}
|
||||
|
||||
cp -a ../.pixi/envs/default/* ${conda_env}
|
||||
|
||||
export PATH="${PWD}/${conda_env}/bin:${PATH}"
|
||||
export CONDA_PREFIX="${PWD}/${conda_env}"
|
||||
|
||||
# delete unnecessary stuff
|
||||
rm -rf ${conda_env}/include
|
||||
find ${conda_env} -name \*.a -delete
|
||||
@@ -50,7 +47,7 @@ cmake --build build
|
||||
mkdir -p FreeCAD.app/Contents/MacOS
|
||||
cp build/FreeCAD FreeCAD.app/Contents/MacOS/FreeCAD
|
||||
|
||||
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
|
||||
python_version=$(${conda_env}/bin/python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
|
||||
version_name="FreeCAD_${BUILD_TAG}-macOS-$(uname -m)-${python_version}"
|
||||
application_menu_name="FreeCAD_${BUILD_TAG}"
|
||||
|
||||
@@ -71,7 +68,7 @@ rm -rf ${conda_env}/Library
|
||||
|
||||
if [[ "${SIGN_RELEASE}" == "true" ]]; then
|
||||
# create the signed dmg
|
||||
./macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
|
||||
../../scripts/macos_sign_and_notarize.zsh -p "FreeCAD" -k ${SIGNING_KEY_ID} -o "${version_name}.dmg"
|
||||
else
|
||||
# create the dmg
|
||||
dmgbuild -s dmg_settings.py "FreeCAD" "${version_name}.dmg"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
# Ensure default values are set in defines if they are not already provided
|
||||
defines.setdefault('containing_folder', '.')
|
||||
defines.setdefault('app_name', 'FreeCAD.app')
|
||||
|
||||
@@ -22,44 +22,37 @@ freecad = { path = "." }
|
||||
[feature.package.dependencies]
|
||||
python = ">=3.11,<3.12"
|
||||
|
||||
[feature.package.tasks]
|
||||
create_bundle = 'bash -c "cd $(bash scripts/get_os.bash) && bash create_bundle.sh"'
|
||||
|
||||
## Linux (x86-64)
|
||||
[feature.package.target.linux-64.dependencies]
|
||||
coreutils = "*"
|
||||
|
||||
[feature.package.target.linux-64.tasks]
|
||||
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
|
||||
|
||||
## Linux (aarch64)
|
||||
[feature.package.target.linux-aarch64.dependencies]
|
||||
coreutils = "*"
|
||||
|
||||
[feature.package.target.linux-aarch64.tasks]
|
||||
create_bundle = 'bash -c "cd linux && bash create_bundle.sh"'
|
||||
|
||||
## macOS (Intel)
|
||||
[feature.package.target.osx-64.dependencies]
|
||||
coreutils = "*"
|
||||
dmgbuild = "*"
|
||||
pyobjc-framework-Quartz = "*"
|
||||
sed = "*"
|
||||
|
||||
[feature.package.target.osx-64.tasks]
|
||||
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
|
||||
|
||||
## macOS (Apple Silicon)
|
||||
[feature.package.target.osx-arm64.dependencies]
|
||||
coreutils = "*"
|
||||
dmgbuild = "*"
|
||||
pyobjc-framework-Quartz = "*"
|
||||
sed = "*"
|
||||
|
||||
[feature.package.target.osx-arm64.tasks]
|
||||
create_bundle = 'bash -c "cd osx && bash create_bundle.sh"'
|
||||
|
||||
## Windows dependencies (x86-64)
|
||||
[feature.package.target.win-64.dependencies]
|
||||
git = "*"
|
||||
|
||||
[feature.package.target.win-64.tasks]
|
||||
create_bundle = 'bash -c "cd windows && bash create_bundle.sh"'
|
||||
nsis = { version = "*", build = "*_log*" }
|
||||
7zip = "*"
|
||||
vs2022_win-64 = "*"
|
||||
|
||||
[environments]
|
||||
default = ["freecad"]
|
||||
|
||||
@@ -145,7 +145,6 @@ requirements:
|
||||
- gmsh
|
||||
- graphviz
|
||||
- ifcopenshell
|
||||
- jinja2
|
||||
- lark
|
||||
- lxml
|
||||
- matplotlib-base
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/src/Tools/SubWCRev.py b/src/Tools/SubWCRev.py
|
||||
index 1f3f0a436343..c314003736f1 100644
|
||||
--- a/src/Tools/SubWCRev.py
|
||||
+++ b/src/Tools/SubWCRev.py
|
||||
@@ -523,7 +523,7 @@ def main():
|
||||
inp = open("%s/src/Build/Version.h.in" % (bindir))
|
||||
lines = inp.readlines()
|
||||
inp.close()
|
||||
- lines = i.writeVersion(lines)
|
||||
+ #lines = i.writeVersion(lines)
|
||||
out = open("%s/src/Build/Version.h.out" % (bindir), "w")
|
||||
out.writelines(lines)
|
||||
out.write("\n")
|
||||
@@ -1,53 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import platform
|
||||
from datetime import datetime
|
||||
|
||||
import freecad
|
||||
import FreeCAD
|
||||
|
||||
package_manager = "conda"
|
||||
system = platform.platform().split("-")[0]
|
||||
arch = platform.machine()
|
||||
|
||||
# Windows uses a different syntax
|
||||
if arch == "AMD64":
|
||||
arch = "x86_64"
|
||||
|
||||
if "ARCH" in os.environ:
|
||||
if os.environ["ARCH"] != "":
|
||||
arch = os.environ["ARCH"]
|
||||
|
||||
python_version = platform.python_version().split(".")
|
||||
python_version = "py" + python_version[0] + python_version[1]
|
||||
date = str(datetime.now()).split(" ")[0]
|
||||
|
||||
version_info = FreeCAD.Version()
|
||||
build_version_suffix = FreeCAD.ConfigGet("BuildVersionSuffix")
|
||||
dev_version = version_info[0] + "." + version_info[1] + "." + version_info[2] + build_version_suffix
|
||||
revision = version_info[3].split(" ")[0]
|
||||
|
||||
if system == "macOS":
|
||||
import jinja2
|
||||
print("create plist from template")
|
||||
osx_directory = os.path.join(os.path.dirname(__file__), "..", "osx")
|
||||
with open(os.path.join(osx_directory, "Info.plist.template")) as template_file:
|
||||
template_str = template_file.read()
|
||||
template = jinja2.Template(template_str)
|
||||
rendered_str = template.render( FREECAD_VERSION="{}-{}".format(dev_version, revision),
|
||||
APPLICATION_MENU_NAME="FreeCAD-{}-{}".format(dev_version, revision) )
|
||||
with open(os.path.join(osx_directory, "FreeCAD.app", "Contents", "Info.plist"), "w") as rendered_file:
|
||||
rendered_file.write(rendered_str)
|
||||
|
||||
if "DEPLOY_RELEASE" in os.environ and os.environ["DEPLOY_RELEASE"] == "weekly-builds":
|
||||
dev_version = "weekly-builds"
|
||||
revision_separator = "-"
|
||||
else:
|
||||
revision_separator = ""
|
||||
revision = ""
|
||||
|
||||
bundle_name = f"FreeCAD_{dev_version}{revision_separator}{revision}-{package_manager}-{system}-{arch}-{python_version}"
|
||||
|
||||
with open("bundle_name.txt", "w") as bundle_name_file:
|
||||
bundle_name_file.write(bundle_name)
|
||||
8
package/rattler-build/scripts/get_os.bash
Normal file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
if [[ "$OSTYPE" =~ (msys*|cygwin*|mingw*) ]]; then
|
||||
echo windows
|
||||
elif [[ "$OSTYPE" == darwin* ]]; then
|
||||
echo osx
|
||||
elif [[ "$OSTYPE" == linux* ]]; then
|
||||
echo linux
|
||||
fi
|
||||
@@ -1,56 +0,0 @@
|
||||
set conda_env="fc_env"
|
||||
|
||||
robocopy ..\.pixi\envs\default\* %conda_env% /S /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
|
||||
%conda_env%\python ..\scripts\get_freecad_version.py
|
||||
set /p freecad_version_name= <bundle_name.txt
|
||||
|
||||
echo **********************
|
||||
echo %freecad_version_name%
|
||||
echo **********************
|
||||
|
||||
REM remove arm binaries that fail to extract unless using latest 7zip
|
||||
for /r %conda_env% %%i in (*arm*.exe) do (@echo "%%i will be removed" & @del "%%i")
|
||||
|
||||
set copy_dir="FreeCAD_Conda_Build"
|
||||
mkdir %copy_dir%
|
||||
|
||||
REM Copy Conda's Python and (U)CRT to FreeCAD/bin
|
||||
robocopy %conda_env%\DLLs %copy_dir%\bin\DLLs /S /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Lib %copy_dir%\bin\Lib /XD __pycache__ /S /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Scripts %copy_dir%\bin\Scripts /S /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\python*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\msvc*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\ucrt*.* %copy_dir%\bin\ /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
REM Copy meaningful executables
|
||||
robocopy %conda_env%\Library\bin %copy_dir%\bin\ ccx.exe /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\bin %copy_dir%\bin\ gmsh.exe /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\bin %copy_dir%\bin\ dot.exe /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\bin %copy_dir%\bin\ unflatten.exe /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\mingw-w64\bin * %copy_dir%\bin\ /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
REM Copy Conda's QT5/plugins to FreeCAD/bin
|
||||
robocopy %conda_env%\Library\plugins %copy_dir%\bin\ /S /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\resources %copy_dir%\resources /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\translations %copy_dir%\translations /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
REM get all the dependency .dlls
|
||||
robocopy %conda_env%\Library\bin *.dll %copy_dir%\bin /XF *.pdb /XF api*.* /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
REM Copy FreeCAD build
|
||||
robocopy %conda_env%\Library\bin FreeCAD* %copy_dir%\bin /XF *.pdb /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\data %copy_dir%\data /XF *.txt /S /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\Ext %copy_dir%\Ext /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\lib %copy_dir%\lib /XF *.lib /XF *.prl /XF *.sh /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\Mod %copy_dir%\Mod /S /XD __pycache__ /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
robocopy %conda_env%\Library\doc %copy_dir%\doc ThirdPartyLibraries.html LICENSE.html /MT:%NUMBER_OF_PROCESSORS% > nul
|
||||
REM Apply Patches
|
||||
rename %copy_dir%\bin\Lib\ssl.py ssl-orig.py
|
||||
copy ssl-patch.py %copy_dir%\bin\Lib\ssl.py
|
||||
|
||||
cd %copy_dir%\..
|
||||
ren %copy_dir% %freecad_version_name%
|
||||
dir
|
||||
|
||||
REM if errorlevel1 exit 1
|
||||
|
||||
"%ProgramFiles%\7-Zip\7z.exe" a -t7z -mx9 -mmt=%NUMBER_OF_PROCESSORS% %freecad_version_name%.7z %freecad_version_name%\ -bb
|
||||
certutil -hashfile "%freecad_version_name%.7z" SHA256 > "%freecad_version_name%.7z"-SHA256.txt
|
||||
echo %date%-%time% >>"%freecad_version_name%.7z"-SHA256.txt
|
||||
@@ -3,23 +3,9 @@
|
||||
set -e
|
||||
set -x
|
||||
|
||||
conda_env="fc_env"
|
||||
conda_env="$(pwd)/../.pixi/envs/default/"
|
||||
|
||||
mkdir -p ${conda_env}
|
||||
|
||||
cp -a ../.pixi/envs/default/* ${conda_env}
|
||||
|
||||
export PATH="${PWD}/${conda_env}/bin:${PATH}"
|
||||
export CONDA_PREFIX="${PWD}/${conda_env}"
|
||||
|
||||
# remove arm binaries that fail to extract unless using latest 7zip
|
||||
rm $(find ${conda_env} -name \*arm\*.exe)
|
||||
|
||||
# delete unnecessary stuff
|
||||
rm -rf ${conda_env}/include
|
||||
find ${conda_env} -name \*.a -delete
|
||||
|
||||
copy_dir="FreeCAD_Conda_Build"
|
||||
copy_dir="FreeCAD_Windows"
|
||||
mkdir -p ${copy_dir}/bin
|
||||
|
||||
# Copy Conda's Python and (U)CRT to FreeCAD/bin
|
||||
@@ -48,16 +34,28 @@ cp -a ${conda_env}/Library/lib ${copy_dir}/lib
|
||||
cp -a ${conda_env}/Library/Mod ${copy_dir}/Mod
|
||||
mkdir -p ${copy_dir}/doc
|
||||
cp -a ${conda_env}/Library/doc/{ThirdPartyLibraries.html,LICENSE.html} ${copy_dir}/doc
|
||||
rm -rf ${conda_env}/bin_tmp
|
||||
|
||||
# delete unnecessary stuff
|
||||
find ${copy_dir} -name \*.a -delete
|
||||
find ${copy_dir} -name \*.lib -delete
|
||||
find ${copy_dir} -name \*arm\*.exe -delete # arm binaries that fail to extract unless using latest 7zip
|
||||
|
||||
# Apply Patches
|
||||
mv ${copy_dir}/bin/Lib/ssl.py ssl-orig.py
|
||||
mv ${copy_dir}/bin/Lib/ssl.py .ssl-orig.py
|
||||
cp ssl-patch.py ${copy_dir}/bin/Lib/ssl.py
|
||||
|
||||
echo '[Paths]' >> ${copy_dir}/bin/qt6.conf
|
||||
echo 'Prefix = ../lib/qt6' >> ${copy_dir}/bin/qt6.conf
|
||||
|
||||
python_version=$(python -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
|
||||
# convenient shortcuts to run the binaries
|
||||
if [ -x /c/ProgramData/chocolatey/tools/shimgen.exe ]; then
|
||||
pushd ${copy_dir}
|
||||
/c/ProgramData/chocolatey/tools/shimgen.exe -p bin/freecadcmd.exe -i "$(pwd)/../../../WindowsInstaller/icons/FreeCAD.ico" -o "$(pwd)/FreeCADCmd.exe"
|
||||
/c/ProgramData/chocolatey/tools/shimgen.exe --gui -p bin/freecad.exe -i "$(pwd)/../../../WindowsInstaller/icons/FreeCAD.ico" -o "$(pwd)/FreeCAD.exe"
|
||||
popd
|
||||
fi
|
||||
|
||||
python_version=$("${copy_dir}"/bin/python.exe -c 'import platform; print("py" + platform.python_version_tuple()[0] + platform.python_version_tuple()[1])')
|
||||
version_name="FreeCAD_${BUILD_TAG}-Windows-$(uname -m)-${python_version}"
|
||||
|
||||
echo -e "################"
|
||||
@@ -69,11 +67,37 @@ sed -i '1s/.*/\nLIST OF PACKAGES:/' ${copy_dir}/packages.txt
|
||||
|
||||
mv ${copy_dir} ${version_name}
|
||||
|
||||
"${PROGRAMFILES}/7-Zip/7z.exe" a -t7z -mx9 -mmt=${NUMBER_OF_PROCESSORS} ${version_name}.7z ${version_name} -bb
|
||||
|
||||
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
|
||||
|
||||
if [ "${MAKE_INSTALLER}" == "true" ]; then
|
||||
FILES_FREECAD="$(cygpath -w $(pwd))\\${version_name}"
|
||||
nsis_cpdir=$(pwd)/.nsis_tmp
|
||||
cp -r "${CONDA_PREFIX}/NSIS" "${nsis_cpdir}"
|
||||
# curl -L -o ".nsis-log.zip" http://prdownloads.sourceforge.net/nsis/nsis-3.11-log.zip # we use the log variant of the package already
|
||||
# curl -L -o ".nsis-strlen_8192.zip" "http://prdownloads.sourceforge.net/nsis/nsis-3.11-strlen_8192.zip"
|
||||
curl -L -o ".NsProcess.7z" "https://nsis.sourceforge.io/mediawiki/images/1/18/NsProcess.zip"
|
||||
if [ ! $(echo fc19fc66a5219a233570fafd5daeb0c9b85387b379f6df5ac8898159a57c5944 .NsProcess.7z | sha256sum --check --status) ]; then
|
||||
7z x .NsProcess.7z -o"${nsis_cpdir}" -y
|
||||
mv "${nsis_cpdir}"/Plugin/nsProcess.dll "${nsis_cpdir}"/Plugins/x86-ansi/nsProcess.dll
|
||||
mv "${nsis_cpdir}"/Plugin/nsProcessW.dll "${nsis_cpdir}"/Plugins/x86-unicode/nsProcess.dll
|
||||
"${nsis_cpdir}"/makensis.exe -V4 \
|
||||
-D"ExeFile=${version_name}-installer.exe" \
|
||||
-D"FILES_FREECAD=${FILES_FREECAD}" \
|
||||
-X'SetCompressor /FINAL lzma' \
|
||||
../../WindowsInstaller/FreeCAD-installer.nsi
|
||||
mv ../../WindowsInstaller/${version_name}-installer.exe .
|
||||
sha256sum ${version_name}-installer.exe > ${version_name}-installer.exe-SHA256.txt
|
||||
else
|
||||
echo "Error: Failed to get NsProcess plugin. Aborting installer creation..."
|
||||
fi
|
||||
rm -rf "${nsis_cpdir}"
|
||||
fi
|
||||
|
||||
if [ "${UPLOAD_RELEASE}" == "true" ]; then
|
||||
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
|
||||
fi
|
||||
|
||||
@@ -83,8 +83,11 @@ fi
|
||||
|
||||
# Check for dmgbuild executable
|
||||
if ! command -v dmgbuild &> /dev/null; then
|
||||
echo "Error: dmgbuild not installed. Please install it for example using pip:"
|
||||
echo 'pip3 install "dmgbuild[badge_icons]>=1.6.0,<1.7.0"'
|
||||
echo 'Error: dmgbuild not installed. Please install it'
|
||||
echo '- using pixi:'
|
||||
echo 'pixi g install dmgbuild --with pyobjc-framework-Quartz'
|
||||
echo '- using pip:'
|
||||
echo 'pip3 install dmgbuild pyobjc-framework-Quartz'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -119,8 +122,96 @@ run_codesign "${CONTAINING_FOLDER}/${APP_NAME}"
|
||||
echo "Creating disk image ${DMG_NAME}"
|
||||
dmgbuild -s ${DMG_SETTINGS} -Dcontaining_folder="${CONTAINING_FOLDER}" -Dapp_name="${APP_NAME}" "${VOLUME_NAME}" "${DMG_NAME}"
|
||||
|
||||
# Submit it for notarization (requires that an App Store API Key has been set up in the notarytool)
|
||||
time xcrun notarytool submit --wait --keychain-profile "${KEYCHAIN_PROFILE}" "${DMG_NAME}"
|
||||
ID_FILE="${DMG_NAME}.notarization_id"
|
||||
|
||||
# Assuming that notarization succeeded, it's a good practice to staple that notarization to the DMG
|
||||
xcrun stapler staple "${DMG_NAME}"
|
||||
# Submit it for notarization (requires that an App Store API Key has been set up in the notarytool)
|
||||
# This is a *very slow* process, and occasionally the GitHub runners lose the internet connection for a short time
|
||||
# during the run. So in order to be fault-tolerant, this script polls, instead of using --wait
|
||||
submit_notarization_request() {
|
||||
if [[ -s "${ID_FILE}" ]]; then
|
||||
cat "${ID_FILE}"
|
||||
return
|
||||
fi
|
||||
local out
|
||||
if ! out=$(xcrun notarytool submit --keychain-profile "${KEYCHAIN_PROFILE}" \
|
||||
--output-format json --no-progress "${DMG_NAME}" 2>&1); then
|
||||
print -r -- "$out" >&2
|
||||
return 1
|
||||
fi
|
||||
# We asked for JSON output so we had something stable, but of course parsing JSON with ZSH is ugly, so a quick bit of
|
||||
# Python does it instead...
|
||||
local id
|
||||
id=$(print -r -- "$out" |
|
||||
/usr/bin/python3 -c 'import sys, json; print(json.load(sys.stdin).get("id",""))'
|
||||
)
|
||||
[[ -n "$id" ]] || { print -r -- "Could not parse submission id" >&2; return 1; }
|
||||
print -r -- "$id" > "${ID_FILE}"
|
||||
print -r -- "$id" # ID is a string here, not an integer, so I can't just return it
|
||||
}
|
||||
|
||||
wait_for_notarization_result() {
|
||||
local id="$1" attempt=0
|
||||
while :; do
|
||||
if xcrun notarytool wait "$id" --keychain-profile "${KEYCHAIN_PROFILE}" \
|
||||
--timeout 10m --no-progress >/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
(( attempt++ ))
|
||||
# If the failure was transient (timeout/HTTP/connection) just retry, but make sure to check to see if the problem
|
||||
# was actually that the signing failed before retrying.
|
||||
local tmp_json
|
||||
tmp_json=$(mktemp)
|
||||
trap 'rm -f "$tmp_json"' EXIT INT TERM
|
||||
|
||||
xcrun notarytool info "$id" --keychain-profile "${KEYCHAIN_PROFILE}" --output-format json 2>/dev/null > "$tmp_json"
|
||||
/usr/bin/python3 - "$tmp_json" <<'PY'
|
||||
import sys, json
|
||||
try:
|
||||
with open(sys.argv[1]) as f:
|
||||
s = (json.load(f).get("status") or "").lower()
|
||||
if s in ("invalid", "rejected"):
|
||||
sys.exit(2)
|
||||
else:
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
PY
|
||||
rc=$?
|
||||
|
||||
rm -f "$tmp_json"
|
||||
|
||||
if [[ $rc == 2 ]]; then
|
||||
print -r -- "Notarization was not accepted by Apple:" >&2
|
||||
xcrun notarytool log "$id" --keychain-profile "${KEYCHAIN_PROFILE}" >&2
|
||||
return 3
|
||||
fi
|
||||
|
||||
if [[ $attempt -gt 120 ]]; then
|
||||
print -r -- "🏳️ Notarization is taking too long, bailing out. 🏳️" >&2
|
||||
return 4
|
||||
fi
|
||||
sleep $(( (attempt<6?2**attempt:60) + RANDOM%5 )) # Increasing timeout plus jitter for multi-run safety
|
||||
done
|
||||
}
|
||||
|
||||
if ! id="$(submit_notarization_request)"; then
|
||||
print -r -- "❌ Failed to submit notarization request" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -z "$id" ]]; then
|
||||
print -r -- "❌ Submission succeeded but no ID was returned" >&2
|
||||
exit 1
|
||||
fi
|
||||
print "Notarization submission ID: $id"
|
||||
|
||||
if wait_for_notarization_result "$id"; then
|
||||
print "✅ Notarization succeeded. Stapling..."
|
||||
xcrun stapler staple "${DMG_NAME}"
|
||||
print "Stapled: ${DMG_NAME}"
|
||||
rm -f "${ID_FILE}"
|
||||
else
|
||||
rc=$?
|
||||
print "❌ Notarization failed (code $rc)." >&2
|
||||
exit "$rc"
|
||||
fi
|
||||
@@ -11,12 +11,10 @@ import SubWCRev
|
||||
|
||||
gitInfo = SubWCRev.GitControl()
|
||||
gitInfo.extractInfo("","")
|
||||
gitDescription = os.environ['BUILD_TAG']
|
||||
|
||||
i = open("src/Build/Version.h.cmake")
|
||||
content = []
|
||||
for line in i.readlines():
|
||||
line = line.replace("-${PACKAGE_VERSION_SUFFIX}",gitDescription)
|
||||
line = line.replace("${PACKAGE_WCREF}",gitInfo.rev)
|
||||
line = line.replace("${PACKAGE_WCDATE}",gitInfo.date)
|
||||
line = line.replace("${PACKAGE_WCURL}",gitInfo.url)
|
||||
@@ -28,6 +26,14 @@ with open("src/Build/Version.h.cmake", "w") as o:
|
||||
content.append('#define FCRepositoryBranch "%s"\n' % (gitInfo.branch))
|
||||
o.writelines(content)
|
||||
|
||||
with open("src/Tools/SubWCRev.py", "r") as f:
|
||||
new_subwcrev = f.read()
|
||||
new_subwcrev = new_subwcrev.replace("lines = i.writeVersion(lines)",
|
||||
"#lines = i.writeVersion(lines) # this source package already has git info, we do nothing here")
|
||||
|
||||
with open("src/Tools/SubWCRev.py", "w") as f:
|
||||
f.writelines(new_subwcrev)
|
||||
|
||||
with open(os.sys.argv[1], "w") as f:
|
||||
f.write(f"rev_number: {gitInfo.rev}\n")
|
||||
f.write(f"branch_name: {gitInfo.branch}\n")
|
||||
@@ -35,8 +41,9 @@ with open(os.sys.argv[1], "w") as f:
|
||||
f.write(f"commit_hash: {gitInfo.hash}\n")
|
||||
f.write(f"remote_url: {gitInfo.url}\n")
|
||||
|
||||
p = subprocess.Popen(["git", "-c", "user.name='github-actions[bot]'", "-c", "user.email='41898282+github-actions[bot]@users.noreply.github.com'",
|
||||
"commit", "-a", "-m", "add git information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
p = subprocess.Popen(["git", "-c", "user.name='github-actions[bot]'", "-c",
|
||||
"user.email='41898282+github-actions[bot]@users.noreply.github.com'", "commit", "-a", "-m",
|
||||
"add git version information"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
|
||||
out, err = p.communicate()
|
||||
|
||||
20
pixi.toml
@@ -152,24 +152,24 @@ freecad-stubs = "*"
|
||||
|
||||
## Qt 6 Configuration Presets
|
||||
[target.linux-64.tasks]
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
|
||||
[target.linux-aarch64.tasks]
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-linux-debug" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-linux-release" ], depends-on= ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
|
||||
[target.osx-64.tasks]
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
|
||||
[target.osx-arm64.tasks]
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release", "-DBUILD_REVERSEENGINEERING=OFF" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-macos-debug" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-macos-release" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
|
||||
[target.win-64.tasks]
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-windows-debug", "-DBUILD_REVERSEENGINEERING=OFF", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-windows-release", "-DBUILD_REVERSEENGINEERING=OFF", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-debug = { cmd = [ "cmake", "--preset", "conda-windows-debug", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
configure-release = { cmd = [ "cmake", "--preset", "conda-windows-release", "-DCMAKE_GENERATOR_PLATFORM=", "-DCMAKE_GENERATOR_TOOLSET=" ], depends-on = ["initialize"], env={ CFLAGS="", CXXFLAGS="", DEBUG_CFLAGS="", DEBUG_CXXFLAGS="" }}
|
||||
freecad-debug = { cmd = [ ".pixi/envs/default/Library/bin/FreeCAD.exe" ], depends-on = ["install-debug"]}
|
||||
freecad-release = { cmd = [ ".pixi/envs/default/Library/bin/FreeCAD.exe" ], depends-on = ["install-release"]}
|
||||
|
||||
|
||||
@@ -1,38 +1,34 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Base.PyObjectBase import PyObjectBase
|
||||
from typing import List
|
||||
|
||||
|
||||
class ApplicationDirectories(PyObjectBase):
|
||||
"""
|
||||
App.ApplicationDirectories class.
|
||||
Provides access to the directory versioning methods of its C++ counterpart.
|
||||
|
||||
For the time being this class only provides access to the directory versioning methods of its
|
||||
C++ counterpart. These are all static methods, so no instance is needed. The main methods of
|
||||
These are all static methods, so no instance is needed. The main methods of
|
||||
this class are migrateAllPaths(), usingCurrentVersionConfig(), and versionStringForPath().
|
||||
|
||||
Author: Chris Hennes (chennes@pioneerlibrarysystem.org)
|
||||
Licence: LGPL-2.1-or-later
|
||||
DeveloperDocu: ApplicationDirectories
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def usingCurrentVersionConfig(path:str) -> bool:
|
||||
def usingCurrentVersionConfig(path: str, /) -> bool:
|
||||
"""
|
||||
usingCurrentVersionConfig(path)
|
||||
Determine if a given config path is for the current version of the program.
|
||||
|
||||
Determine if a given config path is for the current version of the program
|
||||
|
||||
path : the path to check
|
||||
Args:
|
||||
path: The path to check.
|
||||
"""
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def migrateAllPaths(paths: List[str]) -> None:
|
||||
def migrateAllPaths(paths: list[str], /) -> None:
|
||||
"""
|
||||
migrateAllPaths(paths)
|
||||
Migrate a set of versionable configuration directories from the given paths to a new version.
|
||||
|
||||
Migrate a set of versionable configuration directories from the given paths to a new
|
||||
version. The new version's directories cannot exist yet, and the old ones *must* exist.
|
||||
The new version's directories cannot exist yet, and the old ones *must* exist.
|
||||
If the old paths are themselves versioned, then the new paths will be placed at the same
|
||||
level in the directory structure (e.g., they will be siblings of each entry in paths).
|
||||
If paths are NOT versioned, the new (versioned) copies will be placed *inside* the
|
||||
@@ -41,6 +37,9 @@ class ApplicationDirectories(PyObjectBase):
|
||||
If the list contains the same path multiple times, the duplicates are ignored, so it is safe
|
||||
to pass the same path multiple times.
|
||||
|
||||
Args:
|
||||
paths: List of paths to migrate from.
|
||||
|
||||
Examples:
|
||||
Running FreeCAD 1.1, /usr/share/FreeCAD/Config/ -> /usr/share/FreeCAD/Config/v1-1/
|
||||
Running FreeCAD 1.1, /usr/share/FreeCAD/Config/v1-1 -> raises exception, path exists
|
||||
@@ -49,60 +48,80 @@ class ApplicationDirectories(PyObjectBase):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def versionStringForPath(major:int, minor:int) -> str:
|
||||
def versionStringForPath(major: int, minor: int, /) -> str:
|
||||
"""
|
||||
versionStringForPath(major, minor) -> str
|
||||
Given a major and minor version number, return the name for a versioned subdirectory.
|
||||
|
||||
Given a major and minor version number, return a string that can be used as the name for a
|
||||
versioned subdirectory. Only returns the version string, not the full path.
|
||||
Args:
|
||||
major: Major version number.
|
||||
minor: Minor version number.
|
||||
|
||||
Returns:
|
||||
A string that can be used as the name for a versioned subdirectory.
|
||||
Only returns the version string, not the full path.
|
||||
"""
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def isVersionedPath(startingPath:str) -> bool:
|
||||
def isVersionedPath(startingPath: str, /) -> bool:
|
||||
"""
|
||||
isVersionedPath(startingPath) -> bool
|
||||
Determine if a given path is versioned.
|
||||
|
||||
Determine if a given path is versioned (that is, if its last component contains
|
||||
something that this class would have created as a versioned subdirectory). Returns true
|
||||
for any path that the *current* version of FreeCAD would recognized as versioned, and false
|
||||
for either something that is not versioned, or something that is versioned but for a later
|
||||
version of FreeCAD.
|
||||
That is, if its last component contains something that this class would have
|
||||
created as a versioned subdirectory).
|
||||
|
||||
Args:
|
||||
startingPath: The path to check.
|
||||
|
||||
Returns:
|
||||
True for any path that the *current* version of FreeCAD would recognize as versioned,
|
||||
and False for either something that is not versioned, or something that is versioned
|
||||
but for a later version of FreeCAD.
|
||||
"""
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def mostRecentAvailableConfigVersion(startingPath:str) -> str:
|
||||
def mostRecentAvailableConfigVersion(startingPath: str, /) -> str:
|
||||
"""
|
||||
mostRecentAvailableConfigVersion(startingPath) -> str
|
||||
|
||||
Given a base path that is expected to contain versioned subdirectories, locate the
|
||||
directory name (*not* the path, only the final component, the version string itself)
|
||||
corresponding to the most recent version of the software, up to and including the current
|
||||
running version, but NOT exceeding it -- any *later* version whose directories exist
|
||||
in the path is ignored. See also mostRecentConfigFromBase().
|
||||
|
||||
Args:
|
||||
startingPath: The path to check.
|
||||
|
||||
Returns:
|
||||
Most recent available dir name (not path).
|
||||
"""
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def mostRecentConfigFromBase(startingPath: str) -> str:
|
||||
def mostRecentConfigFromBase(startingPath: str, /) -> str:
|
||||
"""
|
||||
mostRecentConfigFromBase(startingPath) -> str
|
||||
|
||||
Given a base path that is expected to contained versioned subdirectories, locate the
|
||||
directory corresponding to the most recent version of the software, up to and including
|
||||
the current version, but NOT exceeding it. Returns the complete path, not just the final
|
||||
component. See also mostRecentAvailableConfigVersion().
|
||||
|
||||
Args:
|
||||
startingPath: The base path to check.
|
||||
|
||||
Returns:
|
||||
Most recent available full path (not just dir name).
|
||||
"""
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def migrateConfig(oldPath: str, newPath: str) -> None:
|
||||
def migrateConfig(oldPath: str, newPath: str, /) -> None:
|
||||
"""
|
||||
migrateConfig(oldPath, newPath) -> None
|
||||
|
||||
A utility method to copy all files and directories from oldPath to newPath, handling the
|
||||
case where newPath might itself be a subdirectory of oldPath (and *not* attempting that
|
||||
otherwise-recursive copy).
|
||||
|
||||
Args:
|
||||
oldPath: Path from.
|
||||
newPath: Path to.
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -31,31 +31,38 @@ namespace App
|
||||
|
||||
class Application;
|
||||
|
||||
/// Helper class to manager transaction (i.e. undo/redo)
|
||||
/**
|
||||
* @brief A helper class to manage transactions (i.e. undo/redo).
|
||||
*
|
||||
* An AutoTransaction object is meant to be allocated on the stack and governs
|
||||
* the transactions in that scope.
|
||||
*/
|
||||
class AppExport AutoTransaction
|
||||
{
|
||||
public:
|
||||
/// Private new operator to prevent heap allocation
|
||||
/// Delete the new operator to prevent heap allocation.
|
||||
void* operator new(std::size_t) = delete;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
/**
|
||||
* @brief Construct an auto transaction.
|
||||
*
|
||||
* @param name: optional new transaction name on construction
|
||||
* @param tmpName: if true and a new transaction is setup, the name given is
|
||||
* @param[in] name: optional new transaction name on construction
|
||||
* @param[in] tmpName: if true and a new transaction is setup, the name given is
|
||||
* considered as temporary, and subsequent construction of this class (or
|
||||
* calling Application::setActiveTransaction()) can override the transaction
|
||||
* name.
|
||||
*
|
||||
* The constructor increments an internal counter
|
||||
* (Application::_activeTransactionGuard). The counter prevents any new
|
||||
* active transaction being setup. It also prevents close (i.e. commits) the
|
||||
* current active transaction until it reaches zero. It does not have any
|
||||
* effect on aborting transaction, though.
|
||||
* active transactions being setup. It also prevents to close
|
||||
* (i.e. commits) the current active transaction until it reaches zero. It
|
||||
* does not have any effect on aborting transactions though.
|
||||
*/
|
||||
AutoTransaction(const char* name = nullptr, bool tmpName = false);
|
||||
|
||||
/** Destructor
|
||||
/**
|
||||
* @brief Destruct an auto transaction.
|
||||
*
|
||||
* This destructor decrease an internal counter
|
||||
* (Application::_activeTransactionGuard), and will commit any current
|
||||
@@ -63,15 +70,19 @@ public:
|
||||
*/
|
||||
~AutoTransaction();
|
||||
|
||||
/** Close or abort the transaction
|
||||
/**
|
||||
* @brief Close or abort the transaction.
|
||||
*
|
||||
* This function can be used to explicitly close (i.e. commit) the
|
||||
* transaction, if the current transaction ID matches the one created inside
|
||||
* the constructor. For aborting, it will abort any current transaction
|
||||
* the constructor. For aborting, it will abort any current transaction.
|
||||
*
|
||||
* @param[in] abort: if true, abort the transaction; otherwise, commit it.
|
||||
*/
|
||||
void close(bool abort = false);
|
||||
|
||||
/** Enable/Disable any AutoTransaction instance in the current stack
|
||||
/**
|
||||
* @brief Enable/Disable any AutoTransaction instance on the current stack.
|
||||
*
|
||||
* Once disabled, any empty temporary named transaction is closed. If there
|
||||
* are non-empty or non-temporary named active transaction, it will not be
|
||||
@@ -79,6 +90,8 @@ public:
|
||||
*
|
||||
* This function may be used in, for example, Gui::Document::setEdit() to
|
||||
* allow a transaction live past any command scope.
|
||||
*
|
||||
* @param[in] enable: if true, enable the AutoTransaction; otherwise, disable it.
|
||||
*/
|
||||
static void setEnable(bool enable);
|
||||
|
||||
@@ -87,47 +100,55 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/** Helper class to lock a transaction from being closed or aborted.
|
||||
/**
|
||||
* @brief Helper class to lock a transaction from being closed or aborted.
|
||||
*
|
||||
* The helper class is used to protect some critical transaction from being
|
||||
* The helper class is used to protect some critical transactions from being
|
||||
* closed prematurely, e.g. when deleting some object.
|
||||
*/
|
||||
class AppExport TransactionLocker
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
* @param lock: whether to activate the lock
|
||||
|
||||
/**
|
||||
* @brief Construct a transaction locker.
|
||||
*
|
||||
* @param[in] lock: whether to activate the lock
|
||||
*/
|
||||
TransactionLocker(bool lock = true);
|
||||
|
||||
/** Destructor
|
||||
* Unlock the transaction is this locker is active
|
||||
/**
|
||||
* @brief Destruct a transaction locker.
|
||||
*
|
||||
* Unlock the transaction if this locker is active
|
||||
*/
|
||||
~TransactionLocker();
|
||||
|
||||
/** Activate or deactivate this locker
|
||||
* @param enable: whether to activate the locker
|
||||
/**
|
||||
* @brief Activate or deactivate this locker.
|
||||
*
|
||||
* An internal counter is used to support recursive locker. When activated,
|
||||
* the current active transaction cannot be closed or aborted. But the
|
||||
* closing call (Application::closeActiveTransaction()) will be remembered,
|
||||
* and performed when the internal lock counter reaches zero.
|
||||
*
|
||||
* @param enable: whether to activate the locker
|
||||
*/
|
||||
void activate(bool enable);
|
||||
|
||||
/// Check if the locker is active
|
||||
/// Check if the locker is active.
|
||||
bool isActive() const
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
/// Check if transaction is being locked
|
||||
/// Check if transaction is being locked.
|
||||
static bool isLocked();
|
||||
|
||||
friend class Application;
|
||||
|
||||
public:
|
||||
/// Private new operator to prevent heap allocation
|
||||
/// Delete the new operator to prevent heap allocation.
|
||||
void* operator new(std::size_t) = delete;
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Base.Metadata import export, constmethod
|
||||
from Base.Persistence import Persistence
|
||||
from Base.BoundBox import BoundBox as BoundBoxPy
|
||||
from Base.BoundBox import BoundBox
|
||||
from Base.Vector import Vector
|
||||
from Base.Placement import Placement as PlacementPy
|
||||
from Base.Placement import Placement
|
||||
from Base.Rotation import Rotation
|
||||
from Base.Matrix import Matrix
|
||||
from StringHasher import StringHasher
|
||||
from typing import Any, Final, List, Dict
|
||||
from typing import Any, Final
|
||||
|
||||
|
||||
@export(
|
||||
@@ -15,89 +19,94 @@ from typing import Any, Final, List, Dict
|
||||
)
|
||||
class ComplexGeoData(Persistence):
|
||||
"""
|
||||
Data.ComplexGeoData class.
|
||||
|
||||
Author: Juergen Riegel (Juergen.Riegel@web.de)
|
||||
Licence: LGPL
|
||||
UserDocu: Father of all complex geometric data types
|
||||
Father of all complex geometric data types.
|
||||
"""
|
||||
|
||||
@constmethod
|
||||
def getElementTypes(self) -> List[str]:
|
||||
def getElementTypes(self) -> list[str]:
|
||||
"""
|
||||
Return a list of element types present in the complex geometric data
|
||||
Return a list of element types present in the complex geometric data.
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def countSubElements(self) -> int:
|
||||
"""
|
||||
Return the number of elements of a type
|
||||
Return the number of elements of a type.
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getFacesFromSubElement(self) -> Any:
|
||||
def getFacesFromSubElement(self, ) -> tuple[list[Vector], list[tuple[int, int, int]]]:
|
||||
"""
|
||||
Return vertexes and faces from a sub-element
|
||||
Return vertexes and faces from a sub-element.
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getLinesFromSubElement(self) -> Any:
|
||||
def getLinesFromSubElement(self, ) -> tuple[list[Vector], list[tuple[int, int]]]:
|
||||
"""
|
||||
Return vertexes and lines from a sub-element
|
||||
Return vertexes and lines from a sub-element.
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getPoints(self) -> Any:
|
||||
def getPoints(self) -> tuple[list[Vector], list[Vector]]:
|
||||
"""
|
||||
Return a tuple of points and normals with a given accuracy
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getLines(self) -> Any:
|
||||
def getLines(self) -> tuple[list[Vector], list[tuple[int, int]]]:
|
||||
"""
|
||||
Return a tuple of points and lines with a given accuracy
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getFaces(self) -> Any:
|
||||
def getFaces(self) -> tuple[list[Vector], list[tuple[int, int, int]]]:
|
||||
"""
|
||||
Return a tuple of points and triangles with a given accuracy
|
||||
"""
|
||||
...
|
||||
|
||||
def applyTranslation(self, translation: Vector) -> None:
|
||||
def applyTranslation(self, translation: Vector, /) -> None:
|
||||
"""
|
||||
Apply an additional translation to the placement
|
||||
"""
|
||||
...
|
||||
|
||||
def applyRotation(self, rotation: Rotation) -> None:
|
||||
def applyRotation(self, rotation: Rotation, /) -> None:
|
||||
"""
|
||||
Apply an additional rotation to the placement
|
||||
"""
|
||||
...
|
||||
|
||||
def transformGeometry(self, transformation: Matrix) -> None:
|
||||
def transformGeometry(self, transformation: Matrix, /) -> None:
|
||||
"""
|
||||
Apply a transformation to the underlying geometry
|
||||
"""
|
||||
...
|
||||
|
||||
def setElementName(self, *, element: str, name: str = None, postfix: str = None, overwrite: bool = False, sid: Any = None) -> None:
|
||||
def setElementName(
|
||||
self,
|
||||
*,
|
||||
element: str,
|
||||
name: str = None,
|
||||
postfix: str = None,
|
||||
overwrite: bool = False,
|
||||
sid: Any = None,
|
||||
) -> None:
|
||||
"""
|
||||
setElementName(element,name=None,postfix=None,overwrite=False,sid=None), Set an element name
|
||||
Set an element name.
|
||||
|
||||
element : the original element name, e.g. Edge1, Vertex2
|
||||
name : the new name for the element, None to remove the mapping
|
||||
postfix : postfix of the name that will not be hashed
|
||||
overwrite: if true, it will overwrite exiting name
|
||||
sid : to hash the name any way you want, provide your own string id(s) in this parameter
|
||||
Args:
|
||||
element : the original element name, e.g. Edge1, Vertex2
|
||||
name : the new name for the element, None to remove the mapping
|
||||
postfix : postfix of the name that will not be hashed
|
||||
overwrite: if true, it will overwrite exiting name
|
||||
sid : to hash the name any way you want, provide your own string id(s) in this parameter
|
||||
|
||||
An element can have multiple mapped names. However, a name can only be mapped
|
||||
to one element
|
||||
@@ -105,33 +114,33 @@ class ComplexGeoData(Persistence):
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getElementName(self, name: str, direction: int = 0) -> Any:
|
||||
def getElementName(self, name: str, direction: int = 0, /) -> str:
|
||||
"""
|
||||
getElementName(name,direction=0) - Return a mapped element name or reverse
|
||||
Return a mapped element name or reverse.
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getElementIndexedName(self, name: str) -> Any:
|
||||
def getElementIndexedName(self, name: str, /) -> str | tuple[str, list[int]]:
|
||||
"""
|
||||
getElementIndexedName(name) - Return the indexed element name
|
||||
Return the indexed element name.
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getElementMappedName(self, name: str) -> Any:
|
||||
def getElementMappedName(self, name: str, /) -> str | tuple[str, list[int]]:
|
||||
"""
|
||||
getElementMappedName(name) - Return the mapped element name
|
||||
Return the mapped element name
|
||||
"""
|
||||
...
|
||||
|
||||
BoundBox: Final[BoundBoxPy] = ...
|
||||
BoundBox: Final[BoundBox] = ...
|
||||
"""Get the bounding box (BoundBox) of the complex geometric data."""
|
||||
|
||||
CenterOfGravity: Final[Vector] = ...
|
||||
"""Get the center of gravity"""
|
||||
|
||||
Placement: PlacementPy = ...
|
||||
Placement: Placement = ...
|
||||
"""Get the current transformation of the object as placement"""
|
||||
|
||||
Tag: int = 0
|
||||
@@ -143,10 +152,10 @@ class ComplexGeoData(Persistence):
|
||||
ElementMapSize: Final[int] = 0
|
||||
"""Get the current element map size"""
|
||||
|
||||
ElementMap: Dict[Any, Any] = {}
|
||||
ElementMap: dict[str, str] = {}
|
||||
"""Get/Set a dict of element mapping"""
|
||||
|
||||
ElementReverseMap: Final[Dict[Any, Any]] = {}
|
||||
ElementReverseMap: Final[dict[str, str | list[str]]] = {}
|
||||
"""Get a dict of element reverse mapping"""
|
||||
|
||||
ElementMapVersion: Final[str] = ""
|
||||
|
||||
@@ -179,10 +179,15 @@ App::DatumElement* LocalCoordinateSystem::getDatumElement(const char* role) cons
|
||||
if (featIt != features.end()) {
|
||||
return static_cast<App::DatumElement*>(*featIt);
|
||||
}
|
||||
std::stringstream err;
|
||||
err << "LocalCoordinateSystem \"" << getFullName() << "\" doesn't contain feature with role \""
|
||||
<< role << '"';
|
||||
throw Base::RuntimeError(err.str().c_str());
|
||||
// During restore, if role lookup fails (e.g. timing issues or fallback to internal name),
|
||||
// we suppress the error. The default getSubObject will try to resolve it by Internal Name next.
|
||||
if (!getDocument()->testStatus(App::Document::Restoring)) {
|
||||
std::stringstream err;
|
||||
err << "LocalCoordinateSystem \"" << getFullName()
|
||||
<< "\" doesn't contain feature with role \"" << role << '"';
|
||||
throw Base::RuntimeError(err.str().c_str());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
App::Line* LocalCoordinateSystem::getAxis(const char* role) const
|
||||
|
||||
@@ -1792,6 +1792,12 @@ bool Document::saveToFile(const char* filename) const
|
||||
// realpath is canonical filename i.e. without symlink
|
||||
std::string nativePath = canonical_path(filename);
|
||||
|
||||
// check if file is writeable, then block the save if it is not.
|
||||
Base::FileInfo originalFileInfo(nativePath);
|
||||
if (originalFileInfo.exists() && !originalFileInfo.isWritable()) {
|
||||
throw Base::FileException("Unable to save document because file is marked as read-only or write permission is not available.", originalFileInfo);
|
||||
}
|
||||
|
||||
// make a tmp. file where to save the project data first and then rename to
|
||||
// the actual file name. This may be useful if overwriting an existing file
|
||||
// fails so that the data of the work up to now isn't lost.
|
||||
@@ -2086,6 +2092,16 @@ bool Document::afterRestore(const std::vector<DocumentObject*>& objArray, bool c
|
||||
FC_ERR("Failed to restore " << obj->getFullName() << ": " << e.what());
|
||||
}
|
||||
catch (...) {
|
||||
|
||||
// If a Python exception occurred, it must be cleared immediately.
|
||||
// Otherwise, the interpreter remains in a dirty state, causing
|
||||
// Segfaults later when FreeCAD interacts with Python.
|
||||
if (PyErr_Occurred()) {
|
||||
Base::Console().error("Python error during object restore:\n");
|
||||
PyErr_Print(); // Print the traceback to stderr/Console
|
||||
PyErr_Clear(); // Reset the interpreter state
|
||||
}
|
||||
|
||||
d->addRecomputeLog("Unknown exception on restore", obj);
|
||||
FC_ERR("Failed to restore " << obj->getFullName() << ": " << "unknown exception");
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PropertyContainer import PropertyContainer
|
||||
from DocumentObject import DocumentObject
|
||||
from typing import Final, List, Tuple, Sequence
|
||||
from typing import Final, Sequence
|
||||
|
||||
|
||||
class Document(PropertyContainer):
|
||||
"""
|
||||
This is a Document class
|
||||
Author: Juergen Riegel (FreeCAD@juergen-riegel.net)
|
||||
Licence: LGPL
|
||||
This is the Document class.
|
||||
"""
|
||||
|
||||
DependencyGraph: Final[str] = ""
|
||||
@@ -16,16 +18,16 @@ class Document(PropertyContainer):
|
||||
ActiveObject: Final[DocumentObject] = None
|
||||
"""The last created object in this document"""
|
||||
|
||||
Objects: Final[List[DocumentObject]] = []
|
||||
Objects: Final[list[DocumentObject]] = []
|
||||
"""The list of objects in this document"""
|
||||
|
||||
TopologicalSortedObjects: Final[List[DocumentObject]] = []
|
||||
TopologicalSortedObjects: Final[list[DocumentObject]] = []
|
||||
"""The list of objects in this document in topological sorted order"""
|
||||
|
||||
RootObjects: Final[List[DocumentObject]] = []
|
||||
RootObjects: Final[list[DocumentObject]] = []
|
||||
"""The list of root objects in this document"""
|
||||
|
||||
RootObjectsIgnoreLinks: Final[List[DocumentObject]] = []
|
||||
RootObjectsIgnoreLinks: Final[list[DocumentObject]] = []
|
||||
"""The list of root objects in this document ignoring references from links."""
|
||||
|
||||
UndoMode: int = 0
|
||||
@@ -40,10 +42,10 @@ class Document(PropertyContainer):
|
||||
RedoCount: Final[int] = 0
|
||||
"""Number of possible Redos"""
|
||||
|
||||
UndoNames: Final[List[str]] = []
|
||||
UndoNames: Final[list[str]] = []
|
||||
"""A list of Undo names"""
|
||||
|
||||
RedoNames: Final[List[str]] = []
|
||||
RedoNames: Final[list[str]] = []
|
||||
"""A List of Redo names"""
|
||||
|
||||
Name: Final[str] = ""
|
||||
@@ -55,10 +57,10 @@ class Document(PropertyContainer):
|
||||
HasPendingTransaction: Final[bool] = False
|
||||
"""Check if there is a pending transaction"""
|
||||
|
||||
InList: Final[List["Document"]] = []
|
||||
InList: Final[list[Document]] = []
|
||||
"""A list of all documents that link to this document."""
|
||||
|
||||
OutList: Final[List["Document"]] = []
|
||||
OutList: Final[list[Document]] = []
|
||||
"""A list of all documents that this document links to."""
|
||||
|
||||
Restoring: Final[bool] = False
|
||||
@@ -84,25 +86,25 @@ class Document(PropertyContainer):
|
||||
|
||||
def save(self) -> None:
|
||||
"""
|
||||
Save the document to disk
|
||||
Save the document to disk.
|
||||
"""
|
||||
...
|
||||
|
||||
def saveAs(self) -> None:
|
||||
def saveAs(self, path: str, /) -> None:
|
||||
"""
|
||||
Save the document under a new name to disk
|
||||
Save the document under a new name to disk.
|
||||
"""
|
||||
...
|
||||
|
||||
def saveCopy(self) -> None:
|
||||
def saveCopy(self, path: str, /) -> None:
|
||||
"""
|
||||
Save a copy of the document under a new name to disk
|
||||
Save a copy of the document under a new name to disk.
|
||||
"""
|
||||
...
|
||||
|
||||
def load(self) -> None:
|
||||
def load(self, path: str, /) -> None:
|
||||
"""
|
||||
Load the document from the given path
|
||||
Load the document from the given path.
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -131,37 +133,40 @@ class Document(PropertyContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def getUniqueObjectName(self, objName: str) -> str:
|
||||
def getUniqueObjectName(self, objName: str, /) -> str:
|
||||
"""
|
||||
getUniqueObjectName(objName) -> objName
|
||||
|
||||
Return the same name, or the name made unique, for Example Box -> Box002 if there are conflicting name
|
||||
already in the document.
|
||||
|
||||
ObjName : str
|
||||
Object name.
|
||||
Args:
|
||||
objName: Object name candidate.
|
||||
|
||||
Returns:
|
||||
Unique object name based on objName.
|
||||
"""
|
||||
...
|
||||
|
||||
def mergeProject(self) -> None:
|
||||
def mergeProject(self, path: str, /) -> None:
|
||||
"""
|
||||
Merges this document with another project file
|
||||
Merges this document with another project file.
|
||||
"""
|
||||
...
|
||||
|
||||
def exportGraphviz(self) -> None:
|
||||
def exportGraphviz(self, path: str = None, /) -> str | None:
|
||||
"""
|
||||
Export the dependencies of the objects as graph
|
||||
Export the dependencies of the objects as graph.
|
||||
|
||||
If path is passed, graph is written to it. if not a string is returned.
|
||||
"""
|
||||
...
|
||||
|
||||
def openTransaction(self, name: str) -> None:
|
||||
def openTransaction(self, name: str, /) -> None:
|
||||
"""
|
||||
openTransaction(name) - Open a new Undo/Redo transaction.
|
||||
Open a new Undo/Redo transaction.
|
||||
|
||||
This function no long creates a new transaction, but calls
|
||||
FreeCAD.setActiveTransaction(name) instead, which will auto creates a
|
||||
transaction with the given name when any change happed in any opened document.
|
||||
transaction with the given name when any change happened in any opened document.
|
||||
If more than one document is changed, all newly created transactions will have
|
||||
the same internal ID and will be undo/redo together.
|
||||
"""
|
||||
@@ -181,7 +186,6 @@ class Document(PropertyContainer):
|
||||
|
||||
def addObject(
|
||||
self,
|
||||
*,
|
||||
type: str,
|
||||
name: str = None,
|
||||
objProxy: object = None,
|
||||
@@ -190,24 +194,22 @@ class Document(PropertyContainer):
|
||||
viewType: str = None,
|
||||
) -> DocumentObject:
|
||||
"""
|
||||
addObject(type, name=None, objProxy=None, viewProxy=None, attach=False, viewType=None)
|
||||
Add an object to document.
|
||||
|
||||
Add an object to document
|
||||
|
||||
type (String): the type of the document object to create.
|
||||
name (String): the optional name of the new object.
|
||||
objProxy (Object): the Python binding object to attach to the new document object.
|
||||
viewProxy (Object): the Python binding object to attach the view provider of this object.
|
||||
attach (Boolean): if True, then bind the document object first before adding to the document
|
||||
to allow Python code to override view provider type. Once bound, and before adding to
|
||||
the document, it will try to call Python binding object's attach(obj) method.
|
||||
viewType (String): override the view provider type directly, only effective when attach is False.
|
||||
Args:
|
||||
type: the type of the document object to create.
|
||||
name: the optional name of the new object.
|
||||
objProxy: the Python binding object to attach to the new document object.
|
||||
viewProxy: the Python binding object to attach the view provider of this object.
|
||||
attach: if True, then bind the document object first before adding to the document
|
||||
to allow Python code to override view provider type. Once bound, and before adding to
|
||||
the document, it will try to call Python binding object's attach(obj) method.
|
||||
viewType: override the view provider type directly, only effective when attach is False.
|
||||
"""
|
||||
...
|
||||
|
||||
def addProperty(
|
||||
self,
|
||||
*,
|
||||
type: str,
|
||||
name: str,
|
||||
group: str = "",
|
||||
@@ -216,22 +218,37 @@ class Document(PropertyContainer):
|
||||
read_only: bool = False,
|
||||
hidden: bool = False,
|
||||
locked: bool = False,
|
||||
) -> "Document":
|
||||
enum_vals: list[str] | None = None,
|
||||
) -> Document:
|
||||
"""
|
||||
addProperty(type: string, name: string, group="", doc="", attr=0, read_only=False, hidden=False, locked=False) -- Add a generic property.
|
||||
Add a generic property.
|
||||
|
||||
Args:
|
||||
type: The type of the property to add.
|
||||
name: The name of the property.
|
||||
group: The group to which the property belongs. Defaults to "".
|
||||
doc: The documentation string for the property. Defaults to "".
|
||||
attr: Attribute flags for the property. Defaults to 0.
|
||||
read_only: Whether the property is read-only. Defaults to False.
|
||||
hidden: Whether the property is hidden. Defaults to False.
|
||||
locked: Whether the property is locked. Defaults to False.
|
||||
|
||||
Returns:
|
||||
The document instance with the added property.
|
||||
"""
|
||||
...
|
||||
|
||||
def removeProperty(self, string: str) -> None:
|
||||
def removeProperty(self, name: str, /) -> None:
|
||||
"""
|
||||
removeProperty(string) -- Remove a generic property.
|
||||
Remove a generic property.
|
||||
|
||||
Note, you can only remove user-defined properties but not built-in ones.
|
||||
"""
|
||||
...
|
||||
|
||||
def removeObject(self) -> None:
|
||||
def removeObject(self, name: str, /) -> None:
|
||||
"""
|
||||
Remove an object from the document
|
||||
Remove an object from the document.
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -241,34 +258,39 @@ class Document(PropertyContainer):
|
||||
*,
|
||||
recursive: bool = False,
|
||||
return_all: bool = False,
|
||||
) -> Tuple[DocumentObject, ...]:
|
||||
) -> tuple[DocumentObject, ...]:
|
||||
"""
|
||||
copyObject(object, recursive=False, return_all=False)
|
||||
Copy an object or objects from another document to this document.
|
||||
|
||||
object: can either be a single object or sequence of objects
|
||||
recursive: if True, also recursively copies internal objects
|
||||
return_all: if True, returns all copied objects, or else return only the copied
|
||||
object corresponding to the input objects.
|
||||
Args:
|
||||
object: can either be a single object or sequence of objects
|
||||
recursive: if True, also recursively copies internal objects
|
||||
return_all: if True, returns all copied objects, or else return only the copied
|
||||
object corresponding to the input objects.
|
||||
"""
|
||||
...
|
||||
|
||||
def moveObject(
|
||||
self, object: DocumentObject, with_dependencies: bool = False
|
||||
self,
|
||||
object: DocumentObject,
|
||||
with_dependencies: bool = False,
|
||||
/,
|
||||
) -> DocumentObject:
|
||||
"""
|
||||
moveObject(object, bool with_dependencies = False)
|
||||
Transfers an object from another document to this document.
|
||||
|
||||
object: can either a single object or sequence of objects
|
||||
with_dependencies: if True, all internal dependent objects are copied too.
|
||||
Args:
|
||||
object: can either a single object or sequence of objects
|
||||
with_dependencies: if True, all internal dependent objects are copied too.
|
||||
"""
|
||||
...
|
||||
|
||||
def importLinks(self, object: DocumentObject = None) -> Tuple[DocumentObject, ...]:
|
||||
def importLinks(
|
||||
self,
|
||||
object: DocumentObject = None,
|
||||
/,
|
||||
) -> tuple[DocumentObject, ...]:
|
||||
"""
|
||||
importLinks(object|[object...])
|
||||
|
||||
Import any externally linked object given a list of objects in
|
||||
this document. Any link type properties of the input objects
|
||||
will be automatically reassigned to the imported object
|
||||
@@ -302,7 +324,7 @@ class Document(PropertyContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def setClosable(self, closable: bool) -> None:
|
||||
def setClosable(self, closable: bool, /) -> None:
|
||||
"""
|
||||
Set a flag that allows or forbids to close a document
|
||||
"""
|
||||
@@ -314,7 +336,7 @@ class Document(PropertyContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def setAutoCreated(self, autoCreated: bool) -> None:
|
||||
def setAutoCreated(self, autoCreated: bool, /) -> None:
|
||||
"""
|
||||
Set a flag that indicates if a document is autoCreated
|
||||
"""
|
||||
@@ -326,9 +348,15 @@ class Document(PropertyContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def recompute(self, objs: Sequence[DocumentObject] = None) -> int:
|
||||
def recompute(
|
||||
self,
|
||||
objs: Sequence[DocumentObject] = None,
|
||||
force: bool = False,
|
||||
check_cycle: bool = False,
|
||||
/,
|
||||
) -> int:
|
||||
"""
|
||||
recompute(objs=None): Recompute the document and returns the amount of recomputed features
|
||||
Recompute the document and returns the amount of recomputed features.
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -350,41 +378,55 @@ class Document(PropertyContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def getObject(self, name: str) -> DocumentObject:
|
||||
def getObject(self, name: str, /) -> DocumentObject:
|
||||
"""
|
||||
Return the object with the given name
|
||||
"""
|
||||
...
|
||||
|
||||
def getObjectsByLabel(self, label: str) -> List[DocumentObject]:
|
||||
def getObjectsByLabel(self, label: str, /) -> list[DocumentObject]:
|
||||
"""
|
||||
Return the objects with the given label name.
|
||||
|
||||
NOTE: It's possible that several objects have the same label name.
|
||||
"""
|
||||
...
|
||||
|
||||
def findObjects(
|
||||
self, *, Type: str = None, Name: str = None, Label: str = None
|
||||
) -> List[DocumentObject]:
|
||||
self,
|
||||
Type: str = None,
|
||||
Name: str = None,
|
||||
Label: str = None,
|
||||
) -> list[DocumentObject]:
|
||||
"""
|
||||
findObjects([Type=string], [Name=string], [Label=string]) -> list
|
||||
Return a list of objects that match the specified type, name or label.
|
||||
|
||||
Name and label support regular expressions. All parameters are optional.
|
||||
|
||||
Args:
|
||||
Type: Type of the feature.
|
||||
Name: Name
|
||||
Label: Label
|
||||
"""
|
||||
...
|
||||
|
||||
def getLinksTo(
|
||||
self, obj: DocumentObject, options: int = 0, maxCount: int = 0
|
||||
) -> Tuple[DocumentObject, ...]:
|
||||
self,
|
||||
obj: DocumentObject,
|
||||
options: int = 0,
|
||||
maxCount: int = 0,
|
||||
/,
|
||||
) -> tuple[DocumentObject, ...]:
|
||||
"""
|
||||
getLinksTo(obj, options=0, maxCount=0): return objects linked to 'obj'
|
||||
Return objects linked to 'obj'
|
||||
|
||||
options: 1: recursive, 2: check link array. Options can combine.
|
||||
maxCount: to limit the number of links returned
|
||||
Args:
|
||||
options: 1: recursive, 2: check link array. Options can combine.
|
||||
maxCount: to limit the number of links returned.
|
||||
"""
|
||||
...
|
||||
|
||||
def supportedTypes(self) -> List[str]:
|
||||
def supportedTypes(self) -> list[str]:
|
||||
"""
|
||||
A list of supported types of objects
|
||||
"""
|
||||
@@ -396,12 +438,11 @@ class Document(PropertyContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def getDependentDocuments(self, sort: bool = True) -> List[DocumentObject]:
|
||||
def getDependentDocuments(self, sort: bool = True, /) -> list[DocumentObject]:
|
||||
"""
|
||||
getDependentDocuments(sort=True)
|
||||
|
||||
Returns a list of documents that this document directly or indirectly links to including itself.
|
||||
|
||||
sort: whether to topologically sort the return list
|
||||
Args:
|
||||
sort: whether to topologically sort the return list
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Base.Metadata import constmethod
|
||||
from Base.Matrix import Matrix
|
||||
from Document import Document
|
||||
@@ -63,7 +67,6 @@ class DocumentObject(ExtensionContainer):
|
||||
|
||||
def addProperty(
|
||||
self,
|
||||
*,
|
||||
type: str,
|
||||
name: str,
|
||||
group: str = "",
|
||||
@@ -72,16 +75,16 @@ class DocumentObject(ExtensionContainer):
|
||||
read_only: bool = False,
|
||||
hidden: bool = False,
|
||||
locked: bool = False,
|
||||
enum_vals: list = []
|
||||
enum_vals: list = [],
|
||||
) -> "DocumentObject":
|
||||
"""
|
||||
addProperty(type: string, name: string, group="", doc="", attr=0, read_only=False, hidden=False, locked = False, enum_vals=[]) -- Add a generic property.
|
||||
Add a generic property.
|
||||
"""
|
||||
...
|
||||
|
||||
def removeProperty(self, string: str) -> None:
|
||||
def removeProperty(self, string: str, /) -> None:
|
||||
"""
|
||||
removeProperty(string) -- Remove a generic property.
|
||||
Remove a generic property.
|
||||
|
||||
Note, you can only remove user-defined properties but not built-in ones.
|
||||
"""
|
||||
@@ -111,28 +114,28 @@ class DocumentObject(ExtensionContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def setExpression(self, name: str, expression: str) -> None:
|
||||
def setExpression(self, name: str, expression: str, /) -> None:
|
||||
"""
|
||||
Register an expression for a property
|
||||
"""
|
||||
...
|
||||
|
||||
def clearExpression(self, name: str) -> None:
|
||||
def clearExpression(self, name: str, /) -> None:
|
||||
"""
|
||||
Clear the expression for a property
|
||||
"""
|
||||
...
|
||||
|
||||
@classmethod
|
||||
def evalExpression(cls, expression: str) -> Any:
|
||||
def evalExpression(cls, expression: str, /) -> Any:
|
||||
"""
|
||||
Evaluate an expression
|
||||
"""
|
||||
...
|
||||
|
||||
def recompute(self, recursive: bool = False) -> None:
|
||||
def recompute(self, recursive: bool = False, /) -> None:
|
||||
"""
|
||||
recompute(recursive=False): Recomputes this object
|
||||
Recomputes this object
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -155,16 +158,14 @@ class DocumentObject(ExtensionContainer):
|
||||
|
||||
def getSubObject(
|
||||
self,
|
||||
*,
|
||||
subname: Union[str, List[str], Tuple[str, ...]],
|
||||
*,
|
||||
retType: int = 0,
|
||||
matrix: Matrix = None,
|
||||
transform: bool = True,
|
||||
depth: int = 0,
|
||||
) -> Any:
|
||||
"""
|
||||
getSubObject(subname, retType=0, matrix=None, transform=True, depth=0)
|
||||
|
||||
* subname(string|list|tuple): dot separated string or sequence of strings
|
||||
referencing subobject.
|
||||
|
||||
@@ -191,17 +192,15 @@ class DocumentObject(ExtensionContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def getSubObjectList(self, subname: str) -> list:
|
||||
def getSubObjectList(self, subname: str, /) -> list:
|
||||
"""
|
||||
getSubObjectList(subname)
|
||||
|
||||
Return a list of objects referenced by a given subname including this object
|
||||
"""
|
||||
...
|
||||
|
||||
def getSubObjects(self, reason: int = 0) -> list:
|
||||
def getSubObjects(self, reason: int = 0, /) -> list:
|
||||
"""
|
||||
getSubObjects(reason=0): Return subname reference of all sub-objects
|
||||
Return subname reference of all sub-objects
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -214,7 +213,6 @@ class DocumentObject(ExtensionContainer):
|
||||
depth: int = 0,
|
||||
) -> Any:
|
||||
"""
|
||||
getLinkedObject(recursive=True, matrix=None, transform=True, depth=0)
|
||||
Returns the linked object if there is one, or else return itself
|
||||
|
||||
* recursive: whether to recursively resolve the links
|
||||
@@ -229,16 +227,16 @@ class DocumentObject(ExtensionContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def setElementVisible(self, element: str, visible: bool) -> int:
|
||||
def setElementVisible(self, element: str, visible: bool, /) -> int:
|
||||
"""
|
||||
setElementVisible(element,visible): Set the visibility of a child element
|
||||
Set the visibility of a child element
|
||||
Return -1 if element visibility is not supported, 0 if element not found, 1 if success
|
||||
"""
|
||||
...
|
||||
|
||||
def isElementVisible(self, element: str) -> int:
|
||||
def isElementVisible(self, element: str, /) -> int:
|
||||
"""
|
||||
isElementVisible(element): Check if a child element is visible
|
||||
Check if a child element is visible
|
||||
Return -1 if element visibility is not supported or element not found, 0 if invisible, or else 1
|
||||
"""
|
||||
...
|
||||
@@ -283,9 +281,9 @@ class DocumentObject(ExtensionContainer):
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def resolve(self, subname: str) -> tuple:
|
||||
def resolve(self, subname: str, /) -> tuple:
|
||||
"""
|
||||
resolve(subname) -- resolve the sub object
|
||||
resolve the sub object
|
||||
|
||||
Returns a tuple (subobj,parent,elementName,subElement), where 'subobj' is the
|
||||
last object referenced in 'subname', and 'parent' is the direct parent of
|
||||
@@ -296,9 +294,9 @@ class DocumentObject(ExtensionContainer):
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def resolveSubElement(self, subname: str, append: bool, type: int) -> tuple:
|
||||
def resolveSubElement(self, subname: str, append: bool, type: int, /) -> tuple:
|
||||
"""
|
||||
resolveSubElement(subname,append,type) -- resolve both new and old style sub element
|
||||
resolve both new and old style sub element
|
||||
|
||||
subname: subname reference containing object hierarchy
|
||||
append: Whether to append object hierarchy prefix inside subname to returned element name
|
||||
@@ -308,24 +306,22 @@ class DocumentObject(ExtensionContainer):
|
||||
"""
|
||||
...
|
||||
|
||||
def adjustRelativeLinks(self, parent: DocumentObject, recursive: bool = True) -> bool:
|
||||
def adjustRelativeLinks(self, parent: DocumentObject, recursive: bool = True, /) -> bool:
|
||||
"""
|
||||
adjustRelativeLinks(parent,recursive=True) -- auto correct potential cyclic dependencies
|
||||
auto correct potential cyclic dependencies
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def getElementMapVersion(self, property_name: str) -> str:
|
||||
def getElementMapVersion(self, property_name: str, /) -> str:
|
||||
"""
|
||||
getElementMapVersion(property_name): return element map version of a given geometry property
|
||||
return element map version of a given geometry property
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def isAttachedToDocument(self) -> bool:
|
||||
"""
|
||||
isAttachedToDocument() -> bool
|
||||
|
||||
Return true if the object is part of a document, false otherwise.
|
||||
"""
|
||||
...
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Extension import Extension
|
||||
|
||||
|
||||
@@ -7,4 +11,5 @@ class DocumentObjectExtension(Extension):
|
||||
Author: Stefan Troeger (stefantroeger@gmx.net)
|
||||
Licence: LGPL
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from DocumentObject import DocumentObject
|
||||
|
||||
|
||||
@@ -7,4 +11,5 @@ class DocumentObjectGroup(DocumentObject):
|
||||
Author: Werner Mayer (wmayer@users.sourceforge.net)
|
||||
Licence: LGPL
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final, Any
|
||||
from Base.PyObjectBase import PyObjectBase
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Base.Metadata import export, constmethod
|
||||
from PropertyContainer import PropertyContainer
|
||||
|
||||
@@ -13,14 +17,14 @@ class ExtensionContainer(PropertyContainer):
|
||||
Licence: LGPL
|
||||
"""
|
||||
|
||||
def addExtension(self, identifier: str) -> None:
|
||||
def addExtension(self, identifier: str, /) -> None:
|
||||
"""
|
||||
Adds an extension to the object. Requires the string identifier for the python extension as argument
|
||||
"""
|
||||
...
|
||||
|
||||
@constmethod
|
||||
def hasExtension(self, identifier: str) -> bool:
|
||||
def hasExtension(self, identifier: str, /) -> bool:
|
||||
"""
|
||||
Returns if this object has the specified extension
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from DocumentObject import DocumentObject
|
||||
from Base import Placement
|
||||
from Base.Placement import Placement
|
||||
from typing import Any, Final, Optional
|
||||
|
||||
|
||||
@@ -19,8 +23,6 @@ class GeoFeature(DocumentObject):
|
||||
|
||||
def getPaths(self) -> Any:
|
||||
"""
|
||||
getPaths()
|
||||
|
||||
Returns all possible paths to the root of the document.
|
||||
Note: Not implemented.
|
||||
"""
|
||||
@@ -28,7 +30,6 @@ class GeoFeature(DocumentObject):
|
||||
|
||||
def getGlobalPlacement(self) -> Placement:
|
||||
"""
|
||||
getGlobalPlacement() -> Base.Placement
|
||||
Deprecated: This function does not handle Links correctly. Use getGlobalPlacementOf instead.
|
||||
|
||||
Returns the placement of the object in the global coordinate space, respecting all stacked
|
||||
@@ -39,16 +40,18 @@ class GeoFeature(DocumentObject):
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def getGlobalPlacementOf(targetObj: Any, rootObj: Any, subname: str) -> Placement:
|
||||
def getGlobalPlacementOf(targetObj: Any, rootObj: Any, subname: str, /) -> Placement:
|
||||
"""
|
||||
getGlobalPlacementOf(targetObj, rootObj, subname) -> Base.Placement
|
||||
Selection example: obj = "part1" sub = "linkToPart2.LinkToBody.Pad.face1"
|
||||
Examples:
|
||||
obj = "part1"
|
||||
sub = "linkToPart2.LinkToBody.Pad.face1"
|
||||
|
||||
Global placement of Pad in this context :
|
||||
getGlobalPlacementOf(pad, part1, "linkToPart2.LinkToBody.Pad.face1")
|
||||
Global placement of Pad in this context:
|
||||
getGlobalPlacementOf(pad, part1, "linkToPart2.LinkToBody.Pad.face1")
|
||||
|
||||
Global placement of linkToPart2 in this context :
|
||||
getGlobalPlacementOf(linkToPart2, part1, "linkToPart2.LinkToBody.Pad.face1")
|
||||
|
||||
Global placement of linkToPart2 in this context:
|
||||
getGlobalPlacementOf(linkToPart2, part1, "linkToPart2.LinkToBody.Pad.face1")
|
||||
|
||||
Returns the placement of the object in the global coordinate space, respecting all stacked
|
||||
relationships.
|
||||
@@ -57,8 +60,6 @@ class GeoFeature(DocumentObject):
|
||||
|
||||
def getPropertyNameOfGeometry(self) -> Optional[str]:
|
||||
"""
|
||||
getPropertyNameOfGeometry() -> str or None
|
||||
|
||||
Returns the property name of the actual geometry.
|
||||
For example for a Part feature it returns the value 'Shape', for a mesh feature the value
|
||||
'Mesh' and so on.
|
||||
@@ -68,8 +69,6 @@ class GeoFeature(DocumentObject):
|
||||
|
||||
def getPropertyOfGeometry(self) -> Optional[Any]:
|
||||
"""
|
||||
getPropertyOfGeometry() -> object or None
|
||||
|
||||
Returns the property of the actual geometry.
|
||||
For example for a Part feature it returns its Shape property, for a Mesh feature its
|
||||
Mesh property and so on.
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from GroupExtension import GroupExtension
|
||||
|
||||
|
||||
@@ -7,4 +11,5 @@ class GeoFeatureGroupExtension(GroupExtension):
|
||||
Licence: LGPL
|
||||
This class handles placeable group of document objects
|
||||
"""
|
||||
|
||||
...
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Base.Metadata import export
|
||||
from DocumentObjectExtension import DocumentObjectExtension
|
||||
from typing import Any, List
|
||||
|
||||
|
||||
@export(
|
||||
Include="App/DocumentObjectGroup.h",
|
||||
)
|
||||
@export(Include="App/DocumentObjectGroup.h", )
|
||||
class GroupExtension(DocumentObjectExtension):
|
||||
"""
|
||||
Extension class which allows grouping of document objects
|
||||
@@ -13,37 +15,37 @@ class GroupExtension(DocumentObjectExtension):
|
||||
Licence: LGPL
|
||||
"""
|
||||
|
||||
def newObject(self, type: str, name: str) -> Any:
|
||||
def newObject(self, type: str, name: str, /) -> Any:
|
||||
"""
|
||||
Create and add an object with given type and name to the group
|
||||
"""
|
||||
...
|
||||
|
||||
def addObject(self, obj: Any) -> List[Any]:
|
||||
def addObject(self, obj: Any, /) -> List[Any]:
|
||||
"""
|
||||
Add an object to the group. Returns all objects that have been added.
|
||||
"""
|
||||
...
|
||||
|
||||
def addObjects(self, objects: List[Any]) -> List[Any]:
|
||||
def addObjects(self, objects: List[Any], /) -> List[Any]:
|
||||
"""
|
||||
Adds multiple objects to the group. Expects a list and returns all objects that have been added.
|
||||
"""
|
||||
...
|
||||
|
||||
def setObjects(self, objects: List[Any]) -> List[Any]:
|
||||
def setObjects(self, objects: List[Any], /) -> List[Any]:
|
||||
"""
|
||||
Sets the objects of the group. Expects a list and returns all objects that are now in the group.
|
||||
"""
|
||||
...
|
||||
|
||||
def removeObject(self, obj: Any) -> List[Any]:
|
||||
def removeObject(self, obj: Any, /) -> List[Any]:
|
||||
"""
|
||||
Remove an object from the group and returns all objects that have been removed.
|
||||
"""
|
||||
...
|
||||
|
||||
def removeObjects(self, objects: List[Any]) -> List[Any]:
|
||||
def removeObjects(self, objects: List[Any], /) -> List[Any]:
|
||||
"""
|
||||
Remove multiple objects from the group. Expects a list and returns all objects that have been removed.
|
||||
"""
|
||||
@@ -55,30 +57,28 @@ class GroupExtension(DocumentObjectExtension):
|
||||
"""
|
||||
...
|
||||
|
||||
def getObject(self, name: str) -> Any:
|
||||
def getObject(self, name: str, /) -> Any:
|
||||
"""
|
||||
Return the object with the given name
|
||||
"""
|
||||
...
|
||||
|
||||
def getObjectsOfType(self, typename: str) -> List[Any]:
|
||||
def getObjectsOfType(self, typename: str, /) -> List[Any]:
|
||||
"""
|
||||
Returns all object in the group of given type
|
||||
@param typename The Freecad type identifier
|
||||
"""
|
||||
...
|
||||
|
||||
def hasObject(self, obj: Any, recursive: bool = False) -> bool:
|
||||
def hasObject(self, obj: Any, recursive: bool = False, /) -> bool:
|
||||
"""
|
||||
hasObject(obj, recursive=false)
|
||||
|
||||
Checks if the group has a given object
|
||||
@param obj the object to check for.
|
||||
@param recursive if true check also if the obj is child of some sub group (default is false).
|
||||
"""
|
||||
...
|
||||
|
||||
def allowObject(self, obj: Any) -> bool:
|
||||
def allowObject(self, obj: Any, /) -> bool:
|
||||
"""
|
||||
Returns true if obj is allowed in the group extension.
|
||||
"""
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from Base.Metadata import export
|
||||
from DocumentObjectExtension import DocumentObjectExtension
|
||||
from typing import Any, Final, List, Tuple, Optional, Union, overload
|
||||
|
||||
|
||||
@export(
|
||||
Include="App/Link.h",
|
||||
)
|
||||
@export(Include="App/Link.h", )
|
||||
class LinkBaseExtension(DocumentObjectExtension):
|
||||
"""
|
||||
Link extension base class
|
||||
@@ -16,15 +18,18 @@ class LinkBaseExtension(DocumentObjectExtension):
|
||||
LinkedChildren: Final[List[Any]] = []
|
||||
"""Return a flattened (in case grouped by plain group) list of linked children"""
|
||||
|
||||
def configLinkProperty(self, **kwargs) -> Any:
|
||||
def configLinkProperty(self, *args, **kwargs) -> Any:
|
||||
"""
|
||||
configLinkProperty(key=val,...): property configuration
|
||||
configLinkProperty(key,...): property configuration with default name
|
||||
Examples:
|
||||
Called with default names:
|
||||
configLinkProperty(prop1, prop2, ..., propN)
|
||||
Called with custom names:
|
||||
configLinkProperty(prop1=val1, prop2=val2, ..., propN=valN)
|
||||
|
||||
This methode is here to implement what I called Property Design
|
||||
This method is here to implement what I called Property Design
|
||||
Pattern. The extension operates on a predefined set of properties,
|
||||
but it relies on the extended object to supply the actual property by
|
||||
calling this methode. You can choose a sub set of functionality of
|
||||
calling this method. You can choose a sub set of functionality of
|
||||
this extension by supplying only some of the supported properties.
|
||||
|
||||
The 'key' are names used to refer to properties supported by this
|
||||
@@ -41,48 +46,48 @@ class LinkBaseExtension(DocumentObjectExtension):
|
||||
"""
|
||||
...
|
||||
|
||||
def getLinkExtProperty(self, name: str) -> Any:
|
||||
def getLinkExtProperty(self, name: str, /) -> Any:
|
||||
"""
|
||||
getLinkExtProperty(name): return the property value by its predefined name
|
||||
return the property value by its predefined name
|
||||
"""
|
||||
...
|
||||
|
||||
def getLinkExtPropertyName(self, name: str) -> str:
|
||||
def getLinkExtPropertyName(self, name: str, /) -> str:
|
||||
"""
|
||||
getLinkExtPropertyName(name): lookup the property name by its predefined name
|
||||
lookup the property name by its predefined name
|
||||
"""
|
||||
...
|
||||
|
||||
@overload
|
||||
def getLinkPropertyInfo(self) -> tuple:
|
||||
def getLinkPropertyInfo(self, /) -> tuple[tuple[str, str, str]]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def getLinkPropertyInfo(self, index: int) -> tuple:
|
||||
def getLinkPropertyInfo(self, index: int, /) -> tuple[str, str, str]:
|
||||
...
|
||||
|
||||
|
||||
@overload
|
||||
def getLinkPropertyInfo(self, name: str) -> tuple:
|
||||
def getLinkPropertyInfo(self, name: str, /) -> tuple[str, str]:
|
||||
...
|
||||
|
||||
def getLinkPropertyInfo(self, arg: Any = None) -> tuple:
|
||||
|
||||
def getLinkPropertyInfo(self, arg: Any = None, /) -> tuple:
|
||||
"""
|
||||
getLinkPropertyInfo(): return a tuple of (name,type,doc) for all supported properties.
|
||||
|
||||
getLinkPropertyInfo(index): return (name,type,doc) of a specific property
|
||||
|
||||
getLinkPropertyInfo(name): return (type,doc) of a specific property
|
||||
Overloads:
|
||||
(): return (name,type,doc) for all supported properties.
|
||||
(index): return (name,type,doc) of a specific property
|
||||
(name): return (type,doc) of a specific property
|
||||
"""
|
||||
...
|
||||
|
||||
def setLink(self, obj: Any, subName: Optional[str] = None, subElements: Optional[Union[str, Tuple[str, ...]]] = None) -> None:
|
||||
def setLink(
|
||||
self,
|
||||
obj: Any,
|
||||
subName: Optional[str] = None,
|
||||
subElements: Optional[Union[str, Tuple[str, ...]]] = None,
|
||||
/,
|
||||
) -> None:
|
||||
"""
|
||||
setLink(obj,subName=None,subElements=None): Set link object.
|
||||
|
||||
setLink([obj,...]),
|
||||
setLink([(obj,subName,subElements),...]),
|
||||
setLink({index:obj,...}),
|
||||
setLink({index:(obj,subName,subElements),...}): set link element of a link group.
|
||||
Called with only obj, set link object, otherwise set link element of a link group.
|
||||
|
||||
obj (DocumentObject): the object to link to. If this is None, then the link is cleared
|
||||
|
||||
@@ -92,27 +97,23 @@ class LinkBaseExtension(DocumentObjectExtension):
|
||||
"""
|
||||
...
|
||||
|
||||
def cacheChildLabel(self, enable: bool = True) -> None:
|
||||
def cacheChildLabel(self, enable: bool = True, /) -> None:
|
||||
"""
|
||||
cacheChildLabel(enable=True): enable/disable child label cache
|
||||
enable/disable child label cache
|
||||
|
||||
The cache is not updated on child label change for performance reason. You must
|
||||
call this function on any child label change
|
||||
"""
|
||||
...
|
||||
|
||||
def flattenSubname(self, subname: str) -> str:
|
||||
def flattenSubname(self, subname: str, /) -> str:
|
||||
"""
|
||||
flattenSubname(subname) -> string
|
||||
|
||||
Return a flattened subname in case it references an object inside a linked plain group
|
||||
"""
|
||||
...
|
||||
|
||||
def expandSubname(self, subname: str) -> str:
|
||||
def expandSubname(self, subname: str, /) -> str:
|
||||
"""
|
||||
expandSubname(subname) -> string
|
||||
|
||||
Return an expanded subname in case it references an object inside a linked plain group
|
||||
"""
|
||||
...
|
||||
|
||||