From 779e1efa55a83443eb7d25d99f5cc0cb70c316e8 Mon Sep 17 00:00:00 2001
From: Furgo <148809153+furgo16@users.noreply.github.com>
Date: Sat, 1 Mar 2025 21:23:52 +0100
Subject: [PATCH 01/31] Start: add option to show only FreeCAD native files in
custom folder
---
src/Mod/Start/App/CustomFolderModel.cpp | 7 +++++++
src/Mod/Start/App/CustomFolderModel.h | 1 +
2 files changed, 8 insertions(+)
diff --git a/src/Mod/Start/App/CustomFolderModel.cpp b/src/Mod/Start/App/CustomFolderModel.cpp
index bd7036e2e1..6225349039 100644
--- a/src/Mod/Start/App/CustomFolderModel.cpp
+++ b/src/Mod/Start/App/CustomFolderModel.cpp
@@ -41,6 +41,8 @@ CustomFolderModel::CustomFolderModel(QObject* parent)
_customFolderDirectory =
QDir(QString::fromStdString(parameterGroup->GetASCII("CustomFolder", "")));
+
+ _showOnlyFCStd = parameterGroup->GetBool("ShowOnlyFCStd", false);
}
void CustomFolderModel::loadCustomFolder()
@@ -52,6 +54,11 @@ void CustomFolderModel::loadCustomFolder()
"BaseApp/Preferences/Mod/Start/CustomFolder: cannot read custom folder %s\n",
_customFolderDirectory.absolutePath().toStdString().c_str());
}
+
+ if (_showOnlyFCStd) {
+ _customFolderDirectory.setNameFilters(QStringList() << QStringLiteral("*.FCStd"));
+ }
+
auto entries = _customFolderDirectory.entryList(QDir::Filter::Files | QDir::Filter::Readable,
QDir::SortFlag::Name);
for (const auto& entry : entries) {
diff --git a/src/Mod/Start/App/CustomFolderModel.h b/src/Mod/Start/App/CustomFolderModel.h
index d4c132708d..3901e1e01d 100644
--- a/src/Mod/Start/App/CustomFolderModel.h
+++ b/src/Mod/Start/App/CustomFolderModel.h
@@ -46,6 +46,7 @@ public:
private:
QDir _customFolderDirectory;
+ bool _showOnlyFCStd; // Show only FreeCAD files
};
} // namespace Start
From cf2e0e06b9496e7aa403064fc04309d0d4b48957 Mon Sep 17 00:00:00 2001
From: Furgo <148809153+furgo16@users.noreply.github.com>
Date: Sun, 2 Mar 2025 07:39:55 +0100
Subject: [PATCH 02/31] Start: add option to show only FCStd files to the
preferences dialog
---
src/Mod/Start/Gui/DlgStartPreferences.ui | 52 +++++++++++++++-----
src/Mod/Start/Gui/DlgStartPreferencesImp.cpp | 2 +
2 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/src/Mod/Start/Gui/DlgStartPreferences.ui b/src/Mod/Start/Gui/DlgStartPreferences.ui
index 2dcf6df394..20f0e5bd1a 100644
--- a/src/Mod/Start/Gui/DlgStartPreferences.ui
+++ b/src/Mod/Start/Gui/DlgStartPreferences.ui
@@ -20,6 +20,20 @@
Contents
+ -
+
+
+ Show only FreeCAD files in additional folder
+
+
+
+ -
+
+
+ Show examples folder contents
+
+
+
-
@@ -27,6 +41,22 @@
+ -
+
+
+ An optional custom folder to be displayed on the Start page.
+
+
+ Gui::FileChooser::Directory
+
+
+ CustomFolder
+
+
+ Mod/Start
+
+
+
-
@@ -49,29 +79,25 @@
- -
-
+
-
+
- An optional custom folder to be displayed on the Start page.
+ If the additional folder contents should include only .FCStd files
-
- Gui::FileChooser::Directory
+
+ Qt::RightToLeft
+
+
+
- CustomFolder
+ ShowOnlyFCStd
Mod/Start
- -
-
-
- Show examples folder contents
-
-
-
diff --git a/src/Mod/Start/Gui/DlgStartPreferencesImp.cpp b/src/Mod/Start/Gui/DlgStartPreferencesImp.cpp
index 0d842d1b23..84b17fae2d 100644
--- a/src/Mod/Start/Gui/DlgStartPreferencesImp.cpp
+++ b/src/Mod/Start/Gui/DlgStartPreferencesImp.cpp
@@ -53,6 +53,7 @@ void DlgStartPreferencesImp::saveSettings()
ui->fileChooserCustomFolder->onSave();
ui->checkBoxShowExamples->onSave();
ui->checkBoxCloseAfterLoading->onSave();
+ ui->checkBoxShowOnlyFCStd->onSave();
}
void DlgStartPreferencesImp::loadSettings()
@@ -60,6 +61,7 @@ void DlgStartPreferencesImp::loadSettings()
ui->fileChooserCustomFolder->onRestore();
ui->checkBoxShowExamples->onRestore();
ui->checkBoxCloseAfterLoading->onRestore();
+ ui->checkBoxShowOnlyFCStd->onRestore();
}
/**
From 1b755ab29d0e3d3f0b68823e81d466508c010f8c Mon Sep 17 00:00:00 2001
From: Alfredo Monclus
Date: Sun, 2 Mar 2025 05:49:08 -0300
Subject: [PATCH 03/31] start: fileview use scrollbars as needed
if for whatever reason the viewport failed to resize let qt add the scrollbars
---
src/Mod/Start/Gui/FileCardView.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Mod/Start/Gui/FileCardView.cpp b/src/Mod/Start/Gui/FileCardView.cpp
index 5b2ab5687d..343a644d9c 100644
--- a/src/Mod/Start/Gui/FileCardView.cpp
+++ b/src/Mod/Start/Gui/FileCardView.cpp
@@ -39,7 +39,7 @@ FileCardView::FileCardView(QWidget* parent)
sizePolicy.setHeightForWidth(true);
setSizePolicy(sizePolicy);
setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
- setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded);
setViewMode(QListView::ViewMode::IconMode);
setFlow(QListView::Flow::LeftToRight);
setResizeMode(QListView::ResizeMode::Adjust);
From 5d97c20cc732a5d4ef1d92f4ae94d9c9a1e87767 Mon Sep 17 00:00:00 2001
From: Joao Matos
Date: Sun, 16 Feb 2025 14:15:00 +0000
Subject: [PATCH 04/31] CI: Refactor generic whitespace checks linting setup.
---
.github/workflows/sub_lint.yml | 107 ++++-----------------
tools/lint/generic_checks.py | 165 +++++++++++++++++++++++++++++++++
tools/lint/utils.py | 95 +++++++++++++++++++
3 files changed, 278 insertions(+), 89 deletions(-)
create mode 100755 tools/lint/generic_checks.py
create mode 100644 tools/lint/utils.py
diff --git a/.github/workflows/sub_lint.yml b/.github/workflows/sub_lint.yml
index ab28e819da..11bb736d37 100644
--- a/.github/workflows/sub_lint.yml
+++ b/.github/workflows/sub_lint.yml
@@ -276,103 +276,32 @@ jobs:
if: inputs.checkLineendings && always()
continue-on-error: ${{ inputs.lineendingsFailSilent }}
run: |
- lineendings=0
- for file in ${{ inputs.changedFiles }}
- do
- # Check for DOS or MAC line endings
- if [[ $(file -b $file) =~ "with CR" ]]
- then
- echo "::warning file=$file,title=$file::File has non Unix line endings"
- echo "$file has non Unix line endings" >> ${{ env.logdir }}lineendings.log
- ((lineendings=lineendings+1))
- fi
- done
- echo "Found $lineendings line ending errors"
- # Write the report
- if [ $lineendings -gt 0 ]
- then
- echo ":information_source: Found $lineendings problems with line endings
" >> ${{env.reportdir}}${{ env.reportfilename }}
- echo "" >> ${{env.reportdir}}${{ env.reportfilename }}
- echo '```' >> ${{env.reportdir}}${{ env.reportfilename }}
- cat ${{ env.logdir }}lineendings.log >> ${{env.reportdir}}${{ env.reportfilename }}
- echo '```' >> ${{env.reportdir}}${{ env.reportfilename }}
- echo " " >> ${{env.reportdir}}${{ env.reportfilename }}
- else
- echo ":heavy_check_mark: No line ending problem found " >> ${{env.reportdir}}${{ env.reportfilename }}
- fi
- echo "" >> ${{env.reportdir}}${{ env.reportfilename }}
- # Exit the step with appropriate code
- [ $lineendings -eq 0 ]
+ python3 tools/lint/generic_checks.py \
+ --lineendings-check \
+ --files "${{ inputs.changedFiles }}" \
+ --report-file "${{ env.reportdir }}${{ env.reportfilename }}" \
+ --log-dir "${{ env.logdir }}"
+
- name: Check for trailing whitespaces
if: inputs.checkWhitespace && always()
continue-on-error: ${{ inputs.whitespaceFailSilent }}
run: |
- whitespaceErrors=0
- exclude="*[.md]"
- for file in ${{ inputs.changedFiles }}
- do
- # Check for trailing whitespaces
- grep -nIHE --exclude="$exclude" " $" $file | sed -e "s/$/<-- trailing whitespace/" >> ${{ env.logdir }}whitespace.log || true
- done
- # Write the Log to the console with the Problem Matchers
- if [ -f ${{ env.logdir }}whitespace.log ]
- then
- echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/grepMatcherWarning.json"
- cat ${{ env.logdir }}whitespace.log
- echo "::remove-matcher owner=grepMatcher-warning::"
- whitespaceErrors=$(wc -l < ${{ env.logdir }}whitespace.log)
- fi
- echo "Found $whitespaceErrors whitespace errors"
- # Write the report
- if [ $whitespaceErrors -gt 0 ]
- then
- echo ":information_source: Found $whitespaceErrors trailing whitespace
" >> ${{env.reportdir}}${{ env.reportfilename }}
- echo "" >> ${{env.reportdir}}${{ env.reportfilename }}
- echo '```' >> ${{env.reportdir}}${{ env.reportfilename }}
- cat ${{ env.logdir }}whitespace.log >> ${{env.reportdir}}${{ env.reportfilename }}
- echo '```' >> ${{env.reportdir}}${{ env.reportfilename }}
- echo " " >> ${{env.reportdir}}${{ env.reportfilename }}
- else
- echo ":heavy_check_mark: No trailing whitespace found " >> ${{env.reportdir}}${{ env.reportfilename }}
- fi
- echo "" >> ${{env.reportdir}}${{ env.reportfilename }}
- # Exit the step with appropriate code
- [ $whitespaceErrors -eq 0 ]
+ python3 tools/lint/generic_checks.py \
+ --whitespace-check \
+ --files "${{ inputs.changedFiles }}" \
+ --report-file "${{ env.reportdir }}${{ env.reportfilename }}" \
+ --log-dir "${{ env.logdir }}"
+
- name: Check for Tab usage
if: inputs.checkTabs && always()
continue-on-error: ${{ inputs.tabsFailSilent }}
run: |
- tabErrors=0
- exclude="*[.md]"
- # Check for Tab usage
- for file in ${{ steps.changed-files.outputs.all_changed_files }}
- do
- grep -nIHE --exclude="$exclude" $'\t' $file | sed -e "s/$/ <-- contains tab/" >> ${{ env.logdir }}tab.log || true
- done
- # Write the Log to the console with the Problem Matchers
- if [ -f ${{ env.logdir }}tab.log ]
- then
- echo "::add-matcher::${{ runner.workspace }}/FreeCAD/.github/problemMatcher/grepMatcherWarning.json"
- cat ${{ env.logdir }}tab.log
- echo "::remove-matcher owner=grepMatcher-warning::"
- tabErrors=$(wc -l < ${{ env.logdir }}tab.log)
- fi
- echo "Found $tabErrors tab errors"
- # Write the report
- if [ $tabErrors -gt 0 ]; then
- echo ":information_source: Found $tabErrors tabs, better to use spaces
" >> ${{env.reportdir}}${{ env.reportfilename }}
- echo "" >> ${{env.reportdir}}${{ env.reportfilename }}
- echo '```' >> ${{env.reportdir}}${{ env.reportfilename }}
- cat ${{ env.logdir }}tab.log >> ${{env.reportdir}}${{ env.reportfilename }}
- echo '```' >> ${{env.reportdir}}${{ env.reportfilename }}
- echo " " >> ${{env.reportdir}}${{ env.reportfilename }}
- else
- echo ":heavy_check_mark: No tabs found" >> ${{env.reportdir}}${{ env.reportfilename }}
- fi
- echo "" >> ${{env.reportdir}}${{ env.reportfilename }}
- # Exit the step with appropriate code
- [ $tabErrors -eq 0 ]
- # Run Python lints
+ python3 tools/lint/generic_checks.py \
+ --tabs-check \
+ --files "${{ inputs.changedFiles }}" \
+ --report-file "${{ env.reportdir }}${{ env.reportfilename }}" \
+ --log-dir "${{ env.logdir }}"
+
- name: Pylint
if: inputs.checkPylint && inputs.changedPythonFiles != '' && always()
continue-on-error: ${{ inputs.pylintFailSilent }}
diff --git a/tools/lint/generic_checks.py b/tools/lint/generic_checks.py
new file mode 100755
index 0000000000..25f1a2d794
--- /dev/null
+++ b/tools/lint/generic_checks.py
@@ -0,0 +1,165 @@
+import argparse
+import glob
+import os
+from utils import (
+ add_common_arguments,
+ init_environment,
+ write_file,
+ emit_problem_matchers,
+)
+
+
+def check_line_endings(file_paths):
+ """Check if files have non-Unix (CRLF or CR) line endings.
+
+ Returns a dict mapping file path to an issue description.
+ """
+ issues = {}
+ for path in file_paths:
+ try:
+ with open(path, "rb") as f:
+ content = f.read()
+ if b"\r\n" in content or (b"\r" in content and b"\n" not in content):
+ issues[path] = "File has non-Unix line endings."
+ except Exception as e:
+ issues[path] = f"Error reading file: {e}"
+ return issues
+
+
+def check_trailing_whitespace(file_paths):
+ """Check for trailing whitespace in files.
+
+ Returns a dict mapping file paths to a list of line numbers with trailing whitespace.
+ """
+ issues = {}
+ for path in file_paths:
+ try:
+ with open(path, "r", encoding="utf-8") as f:
+ lines = f.readlines()
+ error_lines = []
+ for idx, line in enumerate(lines, start=1):
+ if line.rstrip("\n") != line.rstrip():
+ error_lines.append(idx)
+ if error_lines:
+ issues[path] = error_lines
+ except Exception as e:
+ issues[path] = f"Error reading file: {e}"
+ return issues
+
+
+def check_tabs(file_paths):
+ """Check for usage of tab characters in files.
+
+ Returns a dict mapping file paths to a list of line numbers containing tabs.
+ """
+ issues = {}
+ for path in file_paths:
+ try:
+ with open(path, "r", encoding="utf-8") as f:
+ lines = f.readlines()
+ tab_lines = []
+ for idx, line in enumerate(lines, start=1):
+ if "\t" in line:
+ tab_lines.append(idx)
+ if tab_lines:
+ issues[path] = tab_lines
+ except Exception as e:
+ issues[path] = f"Error reading file: {e}"
+ return issues
+
+
+def format_report(section_title, issues):
+ """Format a report section with a Markdown details block."""
+ report = []
+ if issues:
+ count = len(issues)
+ report.append(
+ f":information_source: Found {count} issue(s) in {section_title}
\n"
+ )
+ report.append("```")
+ for file, details in issues.items():
+ report.append(f"{file}: {details}")
+ report.append("```\n \n")
+ else:
+ report.append(f":heavy_check_mark: No issues found in {section_title}\n")
+ return "\n".join(report)
+
+
+def main():
+ parser = argparse.ArgumentParser(description="Run formatting lint checks.")
+ add_common_arguments(parser)
+ parser.add_argument(
+ "--lineendings-check",
+ action="store_true",
+ help="Enable check for non-Unix line endings.",
+ )
+ parser.add_argument(
+ "--whitespace-check",
+ action="store_true",
+ help="Enable check for trailing whitespace.",
+ )
+ parser.add_argument(
+ "--tabs-check", action="store_true", help="Enable check for tab characters."
+ )
+ args = parser.parse_args()
+ init_environment(args)
+
+ file_list = glob.glob(args.files, recursive=True)
+ file_list = [f for f in file_list if os.path.isfile(f)]
+
+ report_sections = []
+
+ # Check non-Unix line endings.
+ if args.lineendings_check:
+ le_issues = check_line_endings(file_list)
+ for file, detail in le_issues.items():
+ print(f"::warning file={file},title={file}::{detail}")
+ report_sections.append(format_report("Non-Unix Line Endings", le_issues))
+
+ # Check trailing whitespace.
+ if args.whitespace_check:
+ ws_issues = check_trailing_whitespace(file_list)
+ if ws_issues:
+ ws_output_lines = []
+ for file, details in ws_issues.items():
+ if isinstance(details, list):
+ for line in details:
+ ws_output_lines.append(f"{file}:{line}: trailing whitespace")
+ else:
+ ws_output_lines.append(f"{file}: {details}")
+ ws_output = "\n".join(ws_output_lines)
+
+ ws_log_file = os.path.join(args.log_dir, "whitespace.log")
+ write_file(ws_log_file, ws_output)
+ emit_problem_matchers(
+ ws_log_file, "grepMatcherWarning.json", "grepMatcher-warning"
+ )
+ report_sections.append(format_report("Trailing Whitespace", ws_issues))
+
+ # Check tab usage.
+ if args.tabs_check:
+ tab_issues = check_tabs(file_list)
+ if tab_issues:
+ tab_output_lines = []
+ for file, details in tab_issues.items():
+ if isinstance(details, list):
+ for line in details:
+ tab_output_lines.append(f"{file}:{line}: contains tab")
+ else:
+ tab_output_lines.append(f"{file}: {details}")
+ tab_output = "\n".join(tab_output_lines)
+
+ tab_log_file = os.path.join(args.log_dir, "tabs.log")
+ write_file(tab_log_file, tab_output)
+ emit_problem_matchers(
+ tab_log_file, "grepMatcherWarning.json", "grepMatcher-warning"
+ )
+ report_sections.append(format_report("Tab Usage", tab_issues))
+
+ report_content = "\n".join(report_sections)
+ write_file(args.report_file, report_content)
+ print("Lint report generated at:", args.report_file)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tools/lint/utils.py b/tools/lint/utils.py
new file mode 100644
index 0000000000..26cc4186a5
--- /dev/null
+++ b/tools/lint/utils.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+import os
+import sys
+import logging
+import subprocess
+import argparse
+from typing import Tuple
+
+
+def run_command(cmd, check=False) -> Tuple[str, str, int]:
+ """
+ Run a command using subprocess.run and return stdout, stderr, and exit code.
+ """
+ try:
+ result = subprocess.run(
+ cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=check, text=True
+ )
+ return result.stdout, result.stderr, result.returncode
+ except subprocess.CalledProcessError as e:
+ return e.stdout, e.stderr, e.returncode
+
+
+def setup_logger(verbose: bool):
+ """
+ Setup the logging level based on the verbose flag.
+ """
+ level = logging.DEBUG if verbose else logging.INFO
+ logging.basicConfig(level=level, format="%(levelname)s: %(message)s")
+
+
+def write_file(file_path: str, content: str):
+ """Write content to the specified file."""
+ with open(file_path, "w", encoding="utf-8") as f:
+ f.write(content)
+ logging.info("Wrote file: %s", file_path)
+
+
+def append_file(file_path: str, content: str):
+ """Append content to the specified file."""
+ with open(file_path, "a", encoding="utf-8") as f:
+ f.write(content + "\n")
+ logging.info("Appended content to file: %s", file_path)
+
+
+def emit_problem_matchers(log_path: str, matcher_filename: str, remove_owner: str):
+ """
+ Emit GitHub Actions problem matcher commands using the given matcher file.
+
+ This function will:
+ 1. Check if the log file exists.
+ 2. Use the RUNNER_WORKSPACE environment variable to construct the matcher path.
+ 3. Print the add-matcher command, then the log content, then the remove-matcher command.
+ """
+ if os.path.isfile(log_path):
+ runner_workspace = os.getenv("RUNNER_WORKSPACE")
+ matcher_path = os.path.join(
+ runner_workspace, "FreeCAD", ".github", "problemMatcher", matcher_filename
+ )
+ print(f"::add-matcher::{matcher_path}")
+ with open(log_path, "r", encoding="utf-8") as f:
+ sys.stdout.write(f.read())
+ print(f"::remove-matcher owner={remove_owner}::")
+
+
+def add_common_arguments(parser: argparse.ArgumentParser) -> argparse.ArgumentParser:
+ """
+ Add common command-line arguments shared between tools.
+ """
+ parser.add_argument(
+ "--files",
+ required=True,
+ help="A space-separated list or glob pattern of files to check.",
+ )
+ parser.add_argument(
+ "--log-dir", required=True, help="Directory where log files will be written."
+ )
+ parser.add_argument(
+ "--report-file",
+ required=True,
+ help="Path to the Markdown report file to append results.",
+ )
+ parser.add_argument("--verbose", action="store_true", help="Enable verbose output.")
+ return parser
+
+
+def init_environment(args: argparse.Namespace):
+ """
+ Perform common initialization tasks:
+ - Set up logging.
+ - Create the log directory.
+ - Create the directory for the report file.
+ """
+ setup_logger(args.verbose)
+ os.makedirs(args.log_dir, exist_ok=True)
+ os.makedirs(os.path.dirname(args.report_file), exist_ok=True)
From 5e72c0afe508a698cadb209af190102b12774005 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jo=C3=A3o=20Matos?=
Date: Sun, 2 Mar 2025 19:47:55 +0000
Subject: [PATCH 05/31] Apply suggestions from code review
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Benjamin Bræstrup Sayoc
---
tools/lint/generic_checks.py | 12 +++---------
1 file changed, 3 insertions(+), 9 deletions(-)
diff --git a/tools/lint/generic_checks.py b/tools/lint/generic_checks.py
index 25f1a2d794..b7eb19675f 100755
--- a/tools/lint/generic_checks.py
+++ b/tools/lint/generic_checks.py
@@ -35,11 +35,8 @@ def check_trailing_whitespace(file_paths):
for path in file_paths:
try:
with open(path, "r", encoding="utf-8") as f:
- lines = f.readlines()
- error_lines = []
- for idx, line in enumerate(lines, start=1):
- if line.rstrip("\n") != line.rstrip():
- error_lines.append(idx)
+ lines = files.read().splitlines()
+ error_lines = [idx if line.endswith(" ") for idx, line in enumerate(lines, start=1)]
if error_lines:
issues[path] = error_lines
except Exception as e:
@@ -57,10 +54,7 @@ def check_tabs(file_paths):
try:
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()
- tab_lines = []
- for idx, line in enumerate(lines, start=1):
- if "\t" in line:
- tab_lines.append(idx)
+ tab_lines = [idx if "\t" in line for idx, line in enumerate(lines, start=1)]
if tab_lines:
issues[path] = tab_lines
except Exception as e:
From b59cd794274c8c007b2ef16c3a06fecf30c0aef1 Mon Sep 17 00:00:00 2001
From: Maxim Moskalets
Date: Mon, 3 Mar 2025 22:30:38 +0300
Subject: [PATCH 06/31] Add support of compile/link jobs pooling
---
.../InitializeFreeCADBuildOptions.cmake | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake
index 6b19043f77..6e8c8e42b0 100644
--- a/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake
+++ b/cMake/FreeCAD_Helpers/InitializeFreeCADBuildOptions.cmake
@@ -13,6 +13,8 @@ macro(InitializeFreeCADBuildOptions)
option(FREECAD_USE_FREETYPE "Builds the features using FreeType libs" ON)
option(FREECAD_BUILD_DEBIAN "Prepare for a build of a Debian package" OFF)
option(FREECAD_CHECK_PIVY "Check for pivy version using Python at build time" ON)
+ option(FREECAD_PARALLEL_COMPILE_JOBS "Compilation jobs pool size to fit memory limitations.")
+ option(FREECAD_PARALLEL_LINK_JOBS "Linkage jobs pool size to fit memory limitations.")
option(BUILD_WITH_CONDA "Set ON if you build FreeCAD with conda" OFF)
option(BUILD_DYNAMIC_LINK_PYTHON "If OFF extension-modules do not link against python-libraries" ON)
option(INSTALL_TO_SITEPACKAGES "If ON the freecad root namespace (python) is installed into python's site-packages" ON)
@@ -206,4 +208,22 @@ macro(InitializeFreeCADBuildOptions)
"Please choose another build directory! Or disable the option BUILD_FORCE_DIRECTORY.")
endif()
endif()
+
+ if(FREECAD_PARALLEL_COMPILE_JOBS)
+ if(CMAKE_GENERATOR MATCHES "Ninja")
+ set_property(GLOBAL APPEND PROPERTY JOB_POOLS compile_job_pool=${FREECAD_PARALLEL_COMPILE_JOBS})
+ set(CMAKE_JOB_POOL_COMPILE compile_job_pool)
+ else()
+ message(WARNING "Job pooling is only available with Ninja generators.")
+ endif()
+ endif()
+
+ if(FREECAD_PARALLEL_LINK_JOBS)
+ if(CMAKE_GENERATOR MATCHES "Ninja")
+ set_property(GLOBAL APPEND PROPERTY JOB_POOLS link_job_pool=${FREECAD_PARALLEL_LINK_JOBS})
+ set(CMAKE_JOB_POOL_LINK link_job_pool)
+ else()
+ message(WARNING "Job pooling is only available with Ninja generators.")
+ endif()
+ endif()
endmacro(InitializeFreeCADBuildOptions)
From 819faf71edc62051cf7966ede16e3820b68b99d6 Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 11:34:27 +1000
Subject: [PATCH 07/31] DRY
---
src/App/Application.cpp | 76 ++++++++++++-----------------------------
1 file changed, 22 insertions(+), 54 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 507b063f51..aefe2ac054 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -394,64 +394,32 @@ void Application::setupPythonTypes()
// clang-format on
}
-// clang-format off
+/*
+ * Define custom Python exception types
+ */
void Application::setupPythonException(PyObject* module)
{
- // Define custom Python exception types
- //
- Base::PyExc_FC_GeneralError = PyErr_NewException("Base.FreeCADError", PyExc_RuntimeError, nullptr);
- Py_INCREF(Base::PyExc_FC_GeneralError);
- PyModule_AddObject(module, "FreeCADError", Base::PyExc_FC_GeneralError);
+ auto setup = [&module, str {"Base."}](const std::string& ename, auto pyExcType) {
+ auto exception = PyErr_NewException((str + ename).c_str(), pyExcType, nullptr);
+ Py_INCREF(exception);
+ PyModule_AddObject(module, ename.c_str(), exception);
+ return exception;
+ };
- Base::PyExc_FC_FreeCADAbort = PyErr_NewException("Base.FreeCADAbort", PyExc_BaseException, nullptr);
- Py_INCREF(Base::PyExc_FC_FreeCADAbort);
- PyModule_AddObject(module, "FreeCADAbort", Base::PyExc_FC_FreeCADAbort);
-
- Base::PyExc_FC_XMLBaseException = PyErr_NewException("Base.XMLBaseException", PyExc_Exception, nullptr);
- Py_INCREF(Base::PyExc_FC_XMLBaseException);
- PyModule_AddObject(module, "XMLBaseException", Base::PyExc_FC_XMLBaseException);
-
- Base::PyExc_FC_XMLParseException = PyErr_NewException("Base.XMLParseException", Base::PyExc_FC_XMLBaseException, nullptr);
- Py_INCREF(Base::PyExc_FC_XMLParseException);
- PyModule_AddObject(module, "XMLParseException", Base::PyExc_FC_XMLParseException);
-
- Base::PyExc_FC_XMLAttributeError = PyErr_NewException("Base.XMLAttributeError", Base::PyExc_FC_XMLBaseException, nullptr);
- Py_INCREF(Base::PyExc_FC_XMLAttributeError);
- PyModule_AddObject(module, "XMLAttributeError", Base::PyExc_FC_XMLAttributeError);
-
- Base::PyExc_FC_UnknownProgramOption = PyErr_NewException("Base.UnknownProgramOption", PyExc_BaseException, nullptr);
- Py_INCREF(Base::PyExc_FC_UnknownProgramOption);
- PyModule_AddObject(module, "UnknownProgramOption", Base::PyExc_FC_UnknownProgramOption);
-
- Base::PyExc_FC_BadFormatError = PyErr_NewException("Base.BadFormatError", Base::PyExc_FC_GeneralError, nullptr);
- Py_INCREF(Base::PyExc_FC_BadFormatError);
- PyModule_AddObject(module, "BadFormatError", Base::PyExc_FC_BadFormatError);
-
- Base::PyExc_FC_BadGraphError = PyErr_NewException("Base.BadGraphError", Base::PyExc_FC_GeneralError, nullptr);
- Py_INCREF(Base::PyExc_FC_BadGraphError);
- PyModule_AddObject(module, "BadGraphError", Base::PyExc_FC_BadGraphError);
-
- Base::PyExc_FC_ExpressionError = PyErr_NewException("Base.ExpressionError", Base::PyExc_FC_GeneralError, nullptr);
- Py_INCREF(Base::PyExc_FC_ExpressionError);
- PyModule_AddObject(module, "ExpressionError", Base::PyExc_FC_ExpressionError);
-
- Base::PyExc_FC_ParserError = PyErr_NewException("Base.ParserError", Base::PyExc_FC_GeneralError, nullptr);
- Py_INCREF(Base::PyExc_FC_ParserError);
- PyModule_AddObject(module, "ParserError", Base::PyExc_FC_ParserError);
-
- Base::PyExc_FC_CADKernelError = PyErr_NewException("Base.CADKernelError", Base::PyExc_FC_GeneralError, nullptr);
- Py_INCREF(Base::PyExc_FC_CADKernelError);
- PyModule_AddObject(module, "CADKernelError", Base::PyExc_FC_CADKernelError);
-
- Base::PyExc_FC_PropertyError = PyErr_NewException("Base.PropertyError", PyExc_AttributeError, nullptr);
- Py_INCREF(Base::PyExc_FC_PropertyError);
- PyModule_AddObject(module, "PropertyError", Base::PyExc_FC_PropertyError);
-
- Base::PyExc_FC_AbortIOException = PyErr_NewException("Base.PyExc_FC_AbortIOException", PyExc_BaseException, nullptr);
- Py_INCREF(Base::PyExc_FC_AbortIOException);
- PyModule_AddObject(module, "AbortIOException", Base::PyExc_FC_AbortIOException);
+ PyExc_FC_GeneralError = setup("FreeCADError", PyExc_RuntimeError);
+ PyExc_FC_FreeCADAbort = setup("FreeCADAbort", PyExc_BaseException);
+ PyExc_FC_XMLBaseException = setup("XMLBaseException", PyExc_Exception);
+ PyExc_FC_XMLParseException = setup("XMLParseException", PyExc_FC_XMLBaseException);
+ PyExc_FC_XMLAttributeError = setup("XMLAttributeError", PyExc_FC_XMLBaseException);
+ PyExc_FC_UnknownProgramOption = setup("UnknownProgramOption", PyExc_BaseException);
+ PyExc_FC_BadFormatError = setup("BadFormatError", PyExc_FC_GeneralError);
+ PyExc_FC_BadGraphError = setup("BadGraphError", PyExc_FC_GeneralError);
+ PyExc_FC_ExpressionError = setup("ExpressionError", PyExc_FC_GeneralError);
+ PyExc_FC_ParserError = setup("ParserError", PyExc_FC_GeneralError);
+ PyExc_FC_CADKernelError = setup("CADKernelError", PyExc_FC_GeneralError);
+ PyExc_FC_PropertyError = setup("PropertyError", PyExc_AttributeError);
+ PyExc_FC_AbortIOException = setup("AbortIOException", PyExc_BaseException);
}
-// clang-format on
//**************************************************************************
// Interface
From c3eee2dc997d274fe0fedb4ad25a45bd6ac5b3ce Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:31:27 +1000
Subject: [PATCH 08/31] Simplify expression
---
src/App/Application.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index aefe2ac054..0fe509646e 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -2668,10 +2668,10 @@ void Application::initConfig(int argc, char ** argv)
_pConsoleObserverFile = nullptr;
// Banner ===========================================================
- if (!(mConfig["RunMode"] == "Cmd")) {
+ if (mConfig["RunMode"] != "Cmd") {
// Remove banner if FreeCAD is invoked via the -c command as regular
// Python interpreter
- if (!(mConfig["Verbose"] == "Strict"))
+ if (mConfig["Verbose"] != "Strict")
Base::Console().Message("%s %s, Libs: %s.%s.%s%sR%s\n%s",
mConfig["ExeName"].c_str(),
mConfig["ExeVersion"].c_str(),
@@ -2797,7 +2797,7 @@ void Application::initApplication()
new Base::ScriptProducer( "FreeCADTest", FreeCADTest );
// creating the application
- if (!(mConfig["Verbose"] == "Strict"))
+ if (mConfig["Verbose"] != "Strict")
Base::Console().Log("Create Application\n");
Application::_pcSingleton = new Application(mConfig);
@@ -3013,7 +3013,7 @@ void Application::LoadParameters()
_pcUserParamMngr->SetSerializer(new ParameterSerializer(mConfig["UserParameter"]));
try {
- if (_pcSysParamMngr->LoadOrCreateDocument() && !(mConfig["Verbose"] == "Strict")) {
+ if (_pcSysParamMngr->LoadOrCreateDocument() && mConfig["Verbose"] != "Strict") {
// Configuration file optional when using as Python module
if (!Py_IsInitialized()) {
Base::Console().Warning(" Parameter does not exist, writing initial one\n");
@@ -3032,7 +3032,7 @@ void Application::LoadParameters()
}
try {
- if (_pcUserParamMngr->LoadOrCreateDocument() && !(mConfig["Verbose"] == "Strict")) {
+ if (_pcUserParamMngr->LoadOrCreateDocument() && mConfig["Verbose"] != "Strict") {
// The user parameter file doesn't exist. When an alternative parameter file is offered
// this will be used.
std::map::iterator it = mConfig.find("UserParameterTemplate");
From 2ab03306cac8c78806f2e30395cb1d3dab088c3e Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:33:03 +1000
Subject: [PATCH 09/31] Use auto
---
src/App/Application.cpp | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 0fe509646e..de2094f275 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -519,7 +519,7 @@ Document* Application::newDocument(const char * proposedName, const char * propo
bool Application::closeDocument(const char* name)
{
- map::iterator pos = DocMap.find( name );
+ auto pos = DocMap.find( name );
if (pos == DocMap.end()) // no such document
return false;
@@ -1214,7 +1214,7 @@ int Application::checkLinkDepth(int depth, MessageOption option)
}
if (depth > _objCount + 2) {
- const char *msg = "Link recursion limit reached. "
+ auto msg = "Link recursion limit reached. "
"Please check for cyclic reference.";
switch (option) {
case MessageOption::Quiet:
@@ -2408,7 +2408,7 @@ void processProgramOptions(const variables_map& vm, std::map Mods = vm["module-path"].as< vector >();
+ auto Mods = vm["module-path"].as< vector >();
string temp;
for (const auto & It : Mods)
temp += It + ";";
@@ -2426,7 +2426,7 @@ void processProgramOptions(const variables_map& vm, std::map Paths = vm["python-path"].as< vector >();
+ auto Paths = vm["python-path"].as< vector >();
for (const auto & It : Paths)
Base::Interpreter().addPythonPath(It.c_str());
}
@@ -2442,7 +2442,7 @@ void processProgramOptions(const variables_map& vm, std::map files(vm["input-file"].as< vector >());
+ auto files(vm["input-file"].as< vector >());
int OpenFileCount=0;
for (const auto & It : files) {
@@ -2510,7 +2510,7 @@ void processProgramOptions(const variables_map& vm, std::map();
+ auto configKey = vm["get-config"].as();
std::stringstream str;
std::map::iterator pos;
pos = mConfig.find(configKey);
@@ -2522,7 +2522,7 @@ void processProgramOptions(const variables_map& vm, std::map configKeyValue = vm["set-config"].as< std::vector >();
+ auto configKeyValue = vm["set-config"].as< std::vector >();
for (const auto& it : configKeyValue) {
auto pos = it.find('=');
if (pos != std::string::npos) {
@@ -2621,9 +2621,9 @@ void Application::initConfig(int argc, char ** argv)
// because the (external) interpreter is already initialized.
// Therefore we use a workaround as described in https://stackoverflow.com/a/57019607
- PyObject *sysModules = PyImport_GetModuleDict();
+ PyObject* sysModules = PyImport_GetModuleDict();
- const char *moduleName = "FreeCAD";
+ auto moduleName = "FreeCAD";
PyImport_AddModule(moduleName);
ApplicationMethods = Application::Methods;
PyObject *pyModule = init_freecad_module();
@@ -2933,8 +2933,8 @@ void Application::processCmdLineFiles()
}
}
- const std::map& cfg = Application::Config();
- std::map::const_iterator it = cfg.find("SaveFile");
+ const std::map& cfg = Application::Config();
+ auto it = cfg.find("SaveFile");
if (it != cfg.end()) {
std::string output = it->second;
output = Base::Tools::escapeEncodeFilename(output);
@@ -3035,7 +3035,7 @@ void Application::LoadParameters()
if (_pcUserParamMngr->LoadOrCreateDocument() && mConfig["Verbose"] != "Strict") {
// The user parameter file doesn't exist. When an alternative parameter file is offered
// this will be used.
- std::map::iterator it = mConfig.find("UserParameterTemplate");
+ auto it = mConfig.find("UserParameterTemplate");
if (it != mConfig.end()) {
QString path = QString::fromUtf8(it->second.c_str());
if (QDir(path).isRelative()) {
From 78696bdaee535bbd1a1faad6981c8e53873bc566 Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:42:29 +1000
Subject: [PATCH 10/31] Join declaration and assignment
---
src/App/Application.cpp | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index de2094f275..40bec112e5 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -554,9 +554,8 @@ void Application::closeAllDocuments()
App::Document* Application::getDocument(const char *Name) const
{
- std::map::const_iterator pos;
- pos = DocMap.find(Name);
+ const auto pos = DocMap.find(Name);
if (pos == DocMap.end())
return nullptr;
@@ -1056,10 +1055,7 @@ void Application::setActiveDocument(const char* Name)
return;
}
- std::map::iterator pos;
- pos = DocMap.find(Name);
-
- if (pos != DocMap.end()) {
+ if (const auto pos = DocMap.find(Name); pos != DocMap.end()) {
setActiveDocument(pos->second);
}
else {
@@ -3484,12 +3480,11 @@ std::string Application::FindHomePath(const char* call)
// FreeCAD shared library.
if (!Py_IsInitialized()) {
uint32_t sz = 0;
- char *buf;
- _NSGetExecutablePath(NULL, &sz); //function only returns "sz" if first arg is to small to hold value
- buf = new char[++sz];
+ _NSGetExecutablePath(
+ nullptr, &sz); // function only returns "sz" if first arg is to small to hold value
- if (_NSGetExecutablePath(buf, &sz) == 0) {
+ if (const auto buf = new char[++sz]; _NSGetExecutablePath(buf, &sz) == 0) {
char resolved[PATH_MAX];
char* path = realpath(buf, resolved);
delete [] buf;
From ee849b634969f723a96216c0e751cace59e9654e Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:44:54 +1000
Subject: [PATCH 11/31] C++ style cast instead of C
---
src/App/Application.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 40bec112e5..613002c026 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -1233,7 +1233,7 @@ std::set Application::getLinksTo(
if(!obj) {
for(auto &v : DocMap) {
v.second->getLinksTo(links,obj,options,maxCount);
- if(maxCount && (int)links.size()>=maxCount)
+ if(maxCount && static_cast(links.size())>=maxCount)
break;
}
} else {
@@ -1241,7 +1241,7 @@ std::set Application::getLinksTo(
for(auto o : obj->getInList()) {
if(o && o->isAttachedToDocument() && docs.insert(o->getDocument()).second) {
o->getDocument()->getLinksTo(links,obj,options,maxCount);
- if(maxCount && (int)links.size()>=maxCount)
+ if(maxCount && static_cast(links.size())>=maxCount)
break;
}
}
@@ -2800,8 +2800,8 @@ void Application::initApplication()
// set up Unit system default
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Units");
- Base::UnitsApi::setSchema((Base::UnitSystem)hGrp->GetInt("UserSchema",0));
- Base::UnitsApi::setDecimals(hGrp->GetInt("Decimals", Base::UnitsApi::getDecimals()));
+ UnitsApi::setSchema(static_cast(hGrp->GetInt("UserSchema", 0)));
+ UnitsApi::setDecimals(hGrp->GetInt("Decimals", UnitsApi::getDecimals()));
// In case we are using fractional inches, get user setting for min unit
int denom = hGrp->GetInt("FracInch", Base::QuantityFormat::getDefaultDenominator());
From 4bfe65b48cbc5882c4ba8cf909cc16f92950ae9e Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:46:10 +1000
Subject: [PATCH 12/31] Declaration hides previous
---
src/App/Application.cpp | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 613002c026..0779767728 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -955,8 +955,8 @@ Document* Application::openDocumentPrivate(const char * FileName,
// objects. To partially solve this problem, we do not
// close and reopen the document immediately here, but
// add it to _pendingDocsReopen to delay reloading.
- for(auto obj : doc->getObjects())
- objNames.emplace_back(obj->getNameInDocument());
+ for(auto obj2 : doc->getObjects())
+ objNames.emplace_back(obj2->getNameInDocument());
_pendingDocMap[doc->FileName.getValue()] = std::move(objNames);
break;
}
@@ -2368,10 +2368,10 @@ void parseProgramOptions(int ac, char ** av, const string& exe, variables_map& v
// Split the file content
char_separator sep(" \n\r");
tokenizer > tok(ss.str(), sep);
- vector args;
- copy(tok.begin(), tok.end(), back_inserter(args));
+ vector args2;
+ copy(tok.begin(), tok.end(), back_inserter(args2));
// Parse the file and store the options
- store( boost::program_options::command_line_parser(args).
+ store( boost::program_options::command_line_parser(args2).
options(cmdline_options).positional(p).extra_parser(Util::customSyntax).run(), vm);
}
}
From 31825c28aa26562305c98482eb11428e75729ccc Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 12:57:34 +1000
Subject: [PATCH 13/31] Else after return
---
src/App/Application.cpp | 22 +++++++++-------------
1 file changed, 9 insertions(+), 13 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 0779767728..ef114ad7d9 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -971,10 +971,13 @@ Document* Application::openDocumentPrivate(const char * FileName,
}
}
- if(!isMainDoc)
+ if (!isMainDoc) {
return nullptr;
- else if(doc)
+ }
+
+ if (doc) {
return doc;
+ }
}
std::string name;
@@ -1266,10 +1269,8 @@ ParameterManager & Application::GetUserParameter()
ParameterManager * Application::GetParameterSet(const char* sName) const
{
auto it = mpcPramManager.find(sName);
- if ( it != mpcPramManager.end() )
- return it->second;
- else
- return nullptr;
+
+ return it != mpcPramManager.end() ? it->second : nullptr;
}
const std::map> &
@@ -3174,13 +3175,8 @@ void getOldDataLocation(std::map& mConfig, std::vector<
*/
QString findUserHomePath(const QString& userHome)
{
- if (userHome.isEmpty()) {
- return getUserHome();
- }
- else {
- return userHome;
- }
-}
+ return userHome.isEmpty() ? getUserHome() : userHome;
+ }
/*!
* \brief findPath
From 8ff4b1b0183138e7fbab8871b5992cf00ddab00a Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 13:07:56 +1000
Subject: [PATCH 14/31] Redundant c_str()
---
src/App/Application.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index ef114ad7d9..eae485173c 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -1163,7 +1163,7 @@ std::string Application::getResourceDir()
{
#ifdef RESOURCEDIR
// #6892: Conda may inject null characters => remove them
- std::string path = std::string(RESOURCEDIR).c_str();
+ auto path = std::string(RESOURCEDIR);
path += PATHSEP;
QDir dir(QString::fromStdString(path));
if (dir.isAbsolute())
@@ -1178,7 +1178,7 @@ std::string Application::getLibraryDir()
{
#ifdef LIBRARYDIR
// #6892: Conda may inject null characters => remove them
- std::string path = std::string(LIBRARYDIR).c_str();
+ auto path = std::string(LIBRARYDIR);
QDir dir(QString::fromStdString(path));
if (dir.isAbsolute())
return path;
@@ -1192,7 +1192,7 @@ std::string Application::getHelpDir()
{
#ifdef DOCDIR
// #6892: Conda may inject null characters => remove them
- std::string path = std::string(DOCDIR).c_str();
+ auto path = std::string(DOCDIR);
path += PATHSEP;
QDir dir(QString::fromStdString(path));
if (dir.isAbsolute())
@@ -1311,7 +1311,7 @@ Base::Reference Application::GetParameterGroupByPath(const char*
cName.erase(0,pos+1);
// test if name is valid
- auto It = mpcPramManager.find(cTemp.c_str());
+ auto It = mpcPramManager.find(cTemp);
if (It == mpcPramManager.end())
throw Base::ValueError("Application::GetParameterGroupByPath() unknown parameter set name specified");
From 4811be9881a026d1bb3fd950746b466126447a17 Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 13:11:23 +1000
Subject: [PATCH 15/31] Reserve vector size
---
src/App/Application.cpp | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index eae485173c..583b5537e6 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -577,6 +577,7 @@ const char * Application::getDocumentName(const App::Document* doc) const
std::vector Application::getDocuments() const
{
std::vector docs;
+ docs.reserve(DocMap.size());
for (const auto & it : DocMap)
docs.push_back(it.second);
return docs;
@@ -1378,8 +1379,10 @@ std::vector Application::getImportModules(const char* Type) const
std::vector Application::getImportModules() const
{
std::vector modules;
- for (const auto & it : _mImportTypes)
+ modules.reserve(_mImportTypes.size());
+ for (const auto& it : _mImportTypes) {
modules.push_back(it.module);
+ }
std::sort(modules.begin(), modules.end());
modules.erase(std::unique(modules.begin(), modules.end()), modules.end());
return modules;
@@ -1501,8 +1504,10 @@ std::vector Application::getExportModules(const char* Type) const
std::vector Application::getExportModules() const
{
std::vector modules;
- for (const auto & it : _mExportTypes)
+ modules.reserve(_mExportTypes.size());
+ for (const auto& it : _mExportTypes) {
modules.push_back(it.module);
+ }
std::sort(modules.begin(), modules.end());
modules.erase(std::unique(modules.begin(), modules.end()), modules.end());
return modules;
From 3ddf2fe67bf2b3b0d11d7156b32180066e2f5bc5 Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 13:17:34 +1000
Subject: [PATCH 16/31] Don't use endl
---
src/App/Application.cpp | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 583b5537e6..2557a45a43 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -1823,11 +1823,11 @@ void printBacktrace(size_t skip=0)
std::stringstream str;
if (status == 0) {
void* offset = (void*)((char*)callstack[i] - (char*)info.dli_saddr);
- str << "#" << (i-skip) << " " << callstack[i] << " in " << demangled << " from " << info.dli_fname << "+" << offset << std::endl;
+ str << "#" << (i-skip) << " " << callstack[i] << " in " << demangled << " from " << info.dli_fname << "+" << offset << '\n';
free(demangled);
}
else {
- str << "#" << (i-skip) << " " << symbols[i] << std::endl;
+ str << "#" << (i-skip) << " " << symbols[i] << '\n';
}
// cannot directly print to cerr when using --write-log
@@ -1856,19 +1856,19 @@ void segmentation_fault_handler(int sig)
#else
switch (sig) {
case SIGSEGV:
- std::cerr << "Illegal storage access..." << std::endl;
+ std::cerr << "Illegal storage access..." << '\n';
#if !defined(_DEBUG)
throw Base::AccessViolation("Illegal storage access! Please save your work under a new file name and restart the application!");
#endif
break;
case SIGABRT:
- std::cerr << "Abnormal program termination..." << std::endl;
+ std::cerr << "Abnormal program termination..." << '\n';
#if !defined(_DEBUG)
throw Base::AbnormalProgramTermination("Break signal occurred");
#endif
break;
default:
- std::cerr << "Unknown error occurred..." << std::endl;
+ std::cerr << "Unknown error occurred..." << '\n';
break;
}
#endif // FC_OS_LINUX
@@ -1876,12 +1876,12 @@ void segmentation_fault_handler(int sig)
void unhandled_exception_handler()
{
- std::cerr << "Terminating..." << std::endl;
+ std::cerr << "Terminating..." << '\n';
}
void unexpection_error_handler()
{
- std::cerr << "Unexpected error occurred..." << std::endl;
+ std::cerr << "Unexpected error occurred..." << '\n';
// try to throw an exception and give the user chance to save their work
#if !defined(_DEBUG)
throw Base::AbnormalProgramTermination("Unexpected error occurred! Please save your work under a new file name and restart the application!");
@@ -2340,21 +2340,21 @@ void parseProgramOptions(int ac, char ** av, const string& exe, variables_map& v
}
catch (const std::exception& e) {
std::stringstream str;
- str << e.what() << endl << endl << visible << endl;
+ str << e.what() << '\n' << '\n' << visible << '\n';
throw Base::UnknownProgramOption(str.str());
}
catch (...) {
std::stringstream str;
- str << "Wrong or unknown option, bailing out!" << endl << endl << visible << endl;
+ str << "Wrong or unknown option, bailing out!" << '\n' << '\n' << visible << '\n';
throw Base::UnknownProgramOption(str.str());
}
if (vm.count("help")) {
std::stringstream str;
- str << exe << endl << endl;
- str << "For a detailed description see https://www.freecad.org/wiki/Start_up_and_Configuration" << endl<() << "'" << endl;
+ << vm["response-file"].as() << "'" << '\n';
throw Base::UnknownProgramOption(str.str());
}
// Read the whole file into a string
@@ -2387,7 +2387,7 @@ void processProgramOptions(const variables_map& vm, std::mapsecond;
}
- str << std::endl;
+ str << '\n';
throw Base::ProgramInformation(str.str());
}
From 035d789ffa477c1643350f54b8630626bcd62c1e Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 13:24:20 +1000
Subject: [PATCH 17/31] Narrowing conversion
---
src/App/Application.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 2557a45a43..c530a30524 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -2717,12 +2717,12 @@ void Application::initConfig(int argc, char ** argv)
#ifdef FC_DEBUG
if (v.second>=0) {
hasDefault = true;
- Base::Console().SetDefaultLogLevel(v.second);
+ Base::Console().SetDefaultLogLevel(static_cast(v.second));
}
#endif
}
else {
- *Base::Console().GetLogLevel(v.first.c_str()) = v.second;
+ *Base::Console().GetLogLevel(v.first.c_str()) = static_cast(v.second);
}
}
@@ -2807,10 +2807,10 @@ void Application::initApplication()
ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
("User parameter:BaseApp/Preferences/Units");
UnitsApi::setSchema(static_cast(hGrp->GetInt("UserSchema", 0)));
- UnitsApi::setDecimals(hGrp->GetInt("Decimals", UnitsApi::getDecimals()));
+ UnitsApi::setDecimals(static_cast(hGrp->GetInt("Decimals", UnitsApi::getDecimals())));
// In case we are using fractional inches, get user setting for min unit
- int denom = hGrp->GetInt("FracInch", Base::QuantityFormat::getDefaultDenominator());
+ int denom = static_cast(hGrp->GetInt("FracInch", Base::QuantityFormat::getDefaultDenominator()));
Base::QuantityFormat::setDefaultDenominator(denom);
From 091f73f09f214f974eda84640db0922769067700 Mon Sep 17 00:00:00 2001
From: bofdahof <172177156+bofdahof@users.noreply.github.com>
Date: Tue, 24 Dec 2024 13:29:36 +1000
Subject: [PATCH 18/31] Calling static via instance
---
src/App/Application.cpp | 72 ++++++++++++++++++++---------------------
1 file changed, 36 insertions(+), 36 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index c530a30524..980f719c24 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -302,12 +302,12 @@ void Application::setupPythonTypes()
// NOTE: To finish the initialization of our own type objects we must
// call PyType_Ready, otherwise we run into a segmentation fault, later on.
// This function is responsible for adding inherited slots from a type's base class.
- Base::Interpreter().addType(&Base::VectorPy::Type, pAppModule, "Vector");
- Base::Interpreter().addType(&Base::MatrixPy::Type, pAppModule, "Matrix");
- Base::Interpreter().addType(&Base::BoundBoxPy::Type, pAppModule, "BoundBox");
- Base::Interpreter().addType(&Base::PlacementPy::Type, pAppModule, "Placement");
- Base::Interpreter().addType(&Base::RotationPy::Type, pAppModule, "Rotation");
- Base::Interpreter().addType(&Base::AxisPy::Type, pAppModule, "Axis");
+ Base::InterpreterSingleton::addType(&Base::VectorPy::Type, pAppModule, "Vector");
+ Base::InterpreterSingleton::addType(&Base::MatrixPy::Type, pAppModule, "Matrix");
+ Base::InterpreterSingleton::addType(&Base::BoundBoxPy::Type, pAppModule, "BoundBox");
+ Base::InterpreterSingleton::addType(&Base::PlacementPy::Type, pAppModule, "Placement");
+ Base::InterpreterSingleton::addType(&Base::RotationPy::Type, pAppModule, "Rotation");
+ Base::InterpreterSingleton::addType(&Base::AxisPy::Type, pAppModule, "Axis");
// Note: Create an own module 'Base' which should provide the python
// binding classes from the base module. At a later stage we should
@@ -324,39 +324,39 @@ void Application::setupPythonTypes()
// Python types
- Base::Interpreter().addType(&Base::VectorPy ::Type,pBaseModule,"Vector");
- Base::Interpreter().addType(&Base::MatrixPy ::Type,pBaseModule,"Matrix");
- Base::Interpreter().addType(&Base::BoundBoxPy ::Type,pBaseModule,"BoundBox");
- Base::Interpreter().addType(&Base::PlacementPy ::Type,pBaseModule,"Placement");
- Base::Interpreter().addType(&Base::RotationPy ::Type,pBaseModule,"Rotation");
- Base::Interpreter().addType(&Base::AxisPy ::Type,pBaseModule,"Axis");
- Base::Interpreter().addType(&Base::CoordinateSystemPy::Type,pBaseModule,"CoordinateSystem");
- Base::Interpreter().addType(&Base::TypePy ::Type,pBaseModule,"TypeId");
- Base::Interpreter().addType(&Base::PrecisionPy ::Type,pBaseModule,"Precision");
+ Base::InterpreterSingleton::addType(&Base::VectorPy ::Type,pBaseModule,"Vector");
+ Base::InterpreterSingleton::addType(&Base::MatrixPy ::Type,pBaseModule,"Matrix");
+ Base::InterpreterSingleton::addType(&Base::BoundBoxPy ::Type,pBaseModule,"BoundBox");
+ Base::InterpreterSingleton::addType(&Base::PlacementPy ::Type,pBaseModule,"Placement");
+ Base::InterpreterSingleton::addType(&Base::RotationPy ::Type,pBaseModule,"Rotation");
+ Base::InterpreterSingleton::addType(&Base::AxisPy ::Type,pBaseModule,"Axis");
+ Base::InterpreterSingleton::addType(&Base::CoordinateSystemPy::Type,pBaseModule,"CoordinateSystem");
+ Base::InterpreterSingleton::addType(&Base::TypePy ::Type,pBaseModule,"TypeId");
+ Base::InterpreterSingleton::addType(&Base::PrecisionPy ::Type,pBaseModule,"Precision");
- Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material");
- Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata");
+ Base::InterpreterSingleton::addType(&MaterialPy::Type, pAppModule, "Material");
+ Base::InterpreterSingleton::addType(&MetadataPy::Type, pAppModule, "Metadata");
- Base::Interpreter().addType(&App::MeasureManagerPy::Type, pAppModule, "MeasureManager");
+ Base::InterpreterSingleton::addType(&MeasureManagerPy::Type, pAppModule, "MeasureManager");
- Base::Interpreter().addType(&App::StringHasherPy::Type, pAppModule, "StringHasher");
- Base::Interpreter().addType(&App::StringIDPy::Type, pAppModule, "StringID");
+ Base::InterpreterSingleton::addType(&StringHasherPy::Type, pAppModule, "StringHasher");
+ Base::InterpreterSingleton::addType(&StringIDPy::Type, pAppModule, "StringID");
// Add document types
- Base::Interpreter().addType(&App::PropertyContainerPy::Type, pAppModule, "PropertyContainer");
- Base::Interpreter().addType(&App::ExtensionContainerPy::Type, pAppModule, "ExtensionContainer");
- Base::Interpreter().addType(&App::DocumentPy::Type, pAppModule, "Document");
- Base::Interpreter().addType(&App::DocumentObjectPy::Type, pAppModule, "DocumentObject");
- Base::Interpreter().addType(&App::DocumentObjectGroupPy::Type, pAppModule, "DocumentObjectGroup");
- Base::Interpreter().addType(&App::GeoFeaturePy::Type, pAppModule, "GeoFeature");
+ Base::InterpreterSingleton::addType(&PropertyContainerPy::Type, pAppModule, "PropertyContainer");
+ Base::InterpreterSingleton::addType(&ExtensionContainerPy::Type, pAppModule, "ExtensionContainer");
+ Base::InterpreterSingleton::addType(&DocumentPy::Type, pAppModule, "Document");
+ Base::InterpreterSingleton::addType(&DocumentObjectPy::Type, pAppModule, "DocumentObject");
+ Base::InterpreterSingleton::addType(&DocumentObjectGroupPy::Type, pAppModule, "DocumentObjectGroup");
+ Base::InterpreterSingleton::addType(&GeoFeaturePy::Type, pAppModule, "GeoFeature");
// Add extension types
- Base::Interpreter().addType(&App::ExtensionPy::Type, pAppModule, "Extension");
- Base::Interpreter().addType(&App::DocumentObjectExtensionPy::Type, pAppModule, "DocumentObjectExtension");
- Base::Interpreter().addType(&App::GroupExtensionPy::Type, pAppModule, "GroupExtension");
- Base::Interpreter().addType(&App::GeoFeatureGroupExtensionPy::Type, pAppModule, "GeoFeatureGroupExtension");
- Base::Interpreter().addType(&App::OriginGroupExtensionPy::Type, pAppModule, "OriginGroupExtension");
- Base::Interpreter().addType(&App::LinkBaseExtensionPy::Type, pAppModule, "LinkBaseExtension");
+ Base::InterpreterSingleton::addType(&ExtensionPy::Type, pAppModule, "Extension");
+ Base::InterpreterSingleton::addType(&DocumentObjectExtensionPy::Type, pAppModule, "DocumentObjectExtension");
+ Base::InterpreterSingleton::addType(&GroupExtensionPy::Type, pAppModule, "GroupExtension");
+ Base::InterpreterSingleton::addType(&GeoFeatureGroupExtensionPy::Type, pAppModule, "GeoFeatureGroupExtension");
+ Base::InterpreterSingleton::addType(&OriginGroupExtensionPy::Type, pAppModule, "OriginGroupExtension");
+ Base::InterpreterSingleton::addType(&LinkBaseExtensionPy::Type, pAppModule, "LinkBaseExtension");
//insert Base and Console
Py_INCREF(pBaseModule);
@@ -377,19 +377,19 @@ void Application::setupPythonTypes()
nullptr, nullptr, nullptr, nullptr
};
PyObject* pUnitsModule = PyModule_Create(&UnitsModuleDef);
- Base::Interpreter().addType(&Base::QuantityPy ::Type,pUnitsModule,"Quantity");
+ Base::InterpreterSingleton::addType(&Base::QuantityPy ::Type,pUnitsModule,"Quantity");
// make sure to set the 'nb_true_divide' slot
- Base::Interpreter().addType(&Base::UnitPy ::Type,pUnitsModule,"Unit");
+ Base::InterpreterSingleton::addType(&Base::UnitPy ::Type,pUnitsModule,"Unit");
Py_INCREF(pUnitsModule);
PyModule_AddObject(pAppModule, "Units", pUnitsModule);
Base::ProgressIndicatorPy::init_type();
- Base::Interpreter().addType(Base::ProgressIndicatorPy::type_object(),
+ Base::InterpreterSingleton::addType(Base::ProgressIndicatorPy::type_object(),
pBaseModule,"ProgressIndicator");
Base::Vector2dPy::init_type();
- Base::Interpreter().addType(Base::Vector2dPy::type_object(),
+ Base::InterpreterSingleton::addType(Base::Vector2dPy::type_object(),
pBaseModule,"Vector2d");
// clang-format on
}
From 66e1710956819002d2e26d089d953d6a5f42c633 Mon Sep 17 00:00:00 2001
From: Chris Hennes
Date: Mon, 3 Mar 2025 16:03:54 -0600
Subject: [PATCH 19/31] App: Minor formatting from review
Co-authored-by: Benjamin Nauck
---
src/App/Application.cpp | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 980f719c24..03aba22b92 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -678,7 +678,7 @@ Document* Application::openDocument(const char * FileName, DocumentCreateFlags c
}
Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCanonical) const {
- if(!path || !path[0])
+ if(!path || path[0] == '\0')
return nullptr;
if(DocFileMap.empty()) {
for(const auto &v : DocMap) {
@@ -2512,7 +2512,7 @@ void processProgramOptions(const variables_map& vm, std::map();
+ auto configKey = vm["get-config"].as();
std::stringstream str;
std::map::iterator pos;
pos = mConfig.find(configKey);
@@ -2695,8 +2695,8 @@ void Application::initConfig(int argc, char ** argv)
if (SafeMode::SafeModeEnabled()) {
Base::Console().Message("FreeCAD is running in _SAFE_MODE_.\n"
- "Safe mode temporarily disables your configurations and "
- "addons. Restart the application to exit safe mode.\n\n");
+ "Safe mode temporarily disables your configurations and "
+ "addons. Restart the application to exit safe mode.\n\n");
}
}
LoadParameters();
@@ -3020,8 +3020,8 @@ void Application::LoadParameters()
if (!Py_IsInitialized()) {
Base::Console().Warning(" Parameter does not exist, writing initial one\n");
Base::Console().Message(" This warning normally means that FreeCAD is running for the first time\n"
- " or the configuration was deleted or moved. FreeCAD is generating the standard\n"
- " configuration.\n");
+ " or the configuration was deleted or moved. FreeCAD is generating the standard\n"
+ " configuration.\n");
}
}
}
@@ -3054,8 +3054,8 @@ void Application::LoadParameters()
if (!Py_IsInitialized()) {
Base::Console().Warning(" User settings do not exist, writing initial one\n");
Base::Console().Message(" This warning normally means that FreeCAD is running for the first time\n"
- " or your configuration was deleted or moved. The system defaults\n"
- " will be automatically generated for you.\n");
+ " or your configuration was deleted or moved. The system defaults\n"
+ " will be automatically generated for you.\n");
}
}
}
@@ -3181,7 +3181,7 @@ void getOldDataLocation(std::map& mConfig, std::vector<
QString findUserHomePath(const QString& userHome)
{
return userHome.isEmpty() ? getUserHome() : userHome;
- }
+}
/*!
* \brief findPath
@@ -3482,8 +3482,8 @@ std::string Application::FindHomePath(const char* call)
if (!Py_IsInitialized()) {
uint32_t sz = 0;
- _NSGetExecutablePath(
- nullptr, &sz); // function only returns "sz" if first arg is to small to hold value
+ // function only returns "sz" if first arg is to small to hold value
+ _NSGetExecutablePath(nullptr, &sz);
if (const auto buf = new char[++sz]; _NSGetExecutablePath(buf, &sz) == 0) {
char resolved[PATH_MAX];
From b090e27dcf2130655866433cd824fa179aa33d31 Mon Sep 17 00:00:00 2001
From: Chris Hennes
Date: Mon, 3 Mar 2025 19:24:15 -0600
Subject: [PATCH 20/31] App: Switch to isNullOrEmpty()
---
src/App/Application.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/App/Application.cpp b/src/App/Application.cpp
index 03aba22b92..c149e20df4 100644
--- a/src/App/Application.cpp
+++ b/src/App/Application.cpp
@@ -678,7 +678,7 @@ Document* Application::openDocument(const char * FileName, DocumentCreateFlags c
}
Document *Application::getDocumentByPath(const char *path, PathMatchMode checkCanonical) const {
- if(!path || path[0] == '\0')
+ if(Base::Tools::isNullOrEmpty(path))
return nullptr;
if(DocFileMap.empty()) {
for(const auto &v : DocMap) {
From 01092f66b1e2f14aba96b7e7372d53cd74750f40 Mon Sep 17 00:00:00 2001
From: Chris Hennes
Date: Mon, 3 Mar 2025 22:43:36 -0600
Subject: [PATCH 21/31] TD: Add missing Boost headers to PCH
---
src/Mod/TechDraw/App/PreCompiled.h | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/Mod/TechDraw/App/PreCompiled.h b/src/Mod/TechDraw/App/PreCompiled.h
index ebf3abca7b..da9dad16b5 100644
--- a/src/Mod/TechDraw/App/PreCompiled.h
+++ b/src/Mod/TechDraw/App/PreCompiled.h
@@ -48,6 +48,10 @@
#include
#include
#include
+#include
+#include
+#include
+#include
#include
// Qt
From 9eca869c95eee9d20867b3d2353f4b1055928f4e Mon Sep 17 00:00:00 2001
From: Roy-043
Date: Tue, 4 Mar 2025 08:58:28 +0100
Subject: [PATCH 22/31] Draft: fix Trimex visibility change
Fixes #19966
---
src/Mod/Draft/draftguitools/gui_trimex.py | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/Mod/Draft/draftguitools/gui_trimex.py b/src/Mod/Draft/draftguitools/gui_trimex.py
index f6a98143d4..4cfab71cac 100644
--- a/src/Mod/Draft/draftguitools/gui_trimex.py
+++ b/src/Mod/Draft/draftguitools/gui_trimex.py
@@ -158,7 +158,6 @@ class Trimex(gui_base_original.Modifier):
self.finish()
_err(translate("draft", "Trimex is not supported yet on this type of object."))
return
- # self.obj.ViewObject.Visibility = False
self.obj.ViewObject.LineColor = (0.5, 0.5, 0.5)
self.obj.ViewObject.LineWidth = 1
self.extrudeMode = False
@@ -393,7 +392,7 @@ class Trimex(gui_base_original.Modifier):
newedges.append(_sh)
ghost.on()
- # resetting the visible edges
+ # resetting the edges
if not reverse:
li = list(range(npoint + 1, len(self.edges)))
else:
@@ -584,7 +583,6 @@ class Trimex(gui_base_original.Modifier):
for g in self.ghost:
g.finalize()
if self.obj:
- self.obj.ViewObject.Visibility = True
if self.color:
self.obj.ViewObject.LineColor = self.color
if self.width:
From 11255e83ad83f80ded4bc054342595a57fbc79d4 Mon Sep 17 00:00:00 2001
From: mosfet80
Date: Tue, 4 Mar 2025 01:21:58 +0100
Subject: [PATCH 23/31] Update actions/stale
https://github.com/actions/stale/releases/tag/v9.1.0
---
.../auto-close_stale_issues_and_pull-requests.yml | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/.github/workflows/auto-close_stale_issues_and_pull-requests.yml b/.github/workflows/auto-close_stale_issues_and_pull-requests.yml
index ffcafbbd90..9433dbe202 100644
--- a/.github/workflows/auto-close_stale_issues_and_pull-requests.yml
+++ b/.github/workflows/auto-close_stale_issues_and_pull-requests.yml
@@ -22,7 +22,7 @@ jobs:
steps:
- name: '🧹 Tag & close stale unconfirmed bugs'
id: stale_issues
- uses: actions/stale@v9.0.0
+ uses: actions/stale@v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
@@ -49,7 +49,7 @@ jobs:
- name: '🧹 Close stale requested feedback issues'
id: awaiting_issues
- uses: actions/stale@v9.0.0
+ uses: actions/stale@v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
@@ -77,7 +77,7 @@ jobs:
- name: '🧹 Tag & close inactive issues'
id: inactive_issues
- uses: actions/stale@v9.0.0
+ uses: actions/stale@v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
@@ -108,7 +108,7 @@ jobs:
- name: '🧹 Tag & close inactive PRs'
id: inactive_pr
- uses: actions/stale@v9.0.0
+ uses: actions/stale@v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: -1
From fc35a58346aee9941424150bf25e7657218ef517 Mon Sep 17 00:00:00 2001
From: Furgo <148809153+furgo16@users.noreply.github.com>
Date: Tue, 4 Mar 2025 11:37:22 +0100
Subject: [PATCH 24/31] BIM: make ifcopenshell version upgrade more robust
(#19823)
* BIM: make ifcopenshell version detection and comparison more robust
* Add reference to FreeCAD bug
* Use create_pip_call for compatibility with different platforms/packages
* Demote workaround logging category for quieter output
* Correct ifcopenshell's actual version in the workaround as well
* Allow upgrading packages
* Provide more verbose output if there is an error with the pip call
* Really print the exception message
* Fix catch-all exception syntax
* Remove ifcopenshell version workaround
Weekly build 40444 containing the fix (https://github.com/FreeCAD/FreeCAD/pull/19861) has been released at https://github.com/FreeCAD/FreeCAD-Bundle/releases/tag/weekly-builds
---
src/Mod/BIM/nativeifc/ifc_openshell.py | 62 +++++++++-----------------
1 file changed, 21 insertions(+), 41 deletions(-)
diff --git a/src/Mod/BIM/nativeifc/ifc_openshell.py b/src/Mod/BIM/nativeifc/ifc_openshell.py
index c0553efb7d..f38b866ee4 100644
--- a/src/Mod/BIM/nativeifc/ifc_openshell.py
+++ b/src/Mod/BIM/nativeifc/ifc_openshell.py
@@ -26,6 +26,8 @@
import FreeCAD
import FreeCADGui
+from packaging.version import Version
+from addonmanager_utilities import create_pip_call
translate = FreeCAD.Qt.translate
QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP
@@ -49,8 +51,7 @@ class IFC_UpdateIOS:
avail = self.get_avail_version()
if avail:
if version:
- comp = self.compare_versions(avail, version)
- if comp > 0:
+ if Version(version) < Version(avail):
self.show_dialog("update", avail)
else:
self.show_dialog("uptodate")
@@ -92,6 +93,7 @@ class IFC_UpdateIOS:
if mode in ["update", "install"]:
result = self.install()
if result:
+ FreeCAD.Console.PrintLog(f"{result.stdout}\n")
text = translate("BIM", "IfcOpenShell update successfully installed.")
buttons = QtGui.QMessageBox.Ok
reply = QtGui.QMessageBox.information(None, title, text, buttons)
@@ -104,7 +106,7 @@ class IFC_UpdateIOS:
from PySide import QtCore, QtGui
QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor)
vendor_path = utils.get_pip_target_directory()
- args = ["install", "--disable-pip-version-check", "--target", vendor_path, "ifcopenshell"]
+ args = ["install", "--upgrade", "--disable-pip-version-check", "--target", vendor_path, "ifcopenshell"]
result = self.run_pip(args)
QtGui.QApplication.restoreOverrideCursor()
return result
@@ -115,14 +117,17 @@ class IFC_UpdateIOS:
import addonmanager_utilities as utils
import freecad.utils
- cmd = [freecad.utils.get_python_exe(), "-m", "pip"]
- cmd.extend(args)
+ from subprocess import CalledProcessError
+
+ cmd = create_pip_call(args)
result = None
try:
result = utils.run_interruptable_subprocess(cmd)
- except:
+ except CalledProcessError as pe:
+ FreeCAD.Console.PrintError(pe.stderr)
+ except Exception as e:
text = translate("BIM","Unable to run pip. Please ensure pip is installed on your system.")
- FreeCAD.Console.PrintError(text + "\n")
+ FreeCAD.Console.PrintError(f"{text} {str(e)}\n")
return result
@@ -130,19 +135,19 @@ class IFC_UpdateIOS:
"""Retrieves the current ifcopenshell version"""
import addonmanager_utilities as utils
+ from packaging.version import InvalidVersion
+
try:
import ifcopenshell
- version = ifcopenshell.version
+ version = ifcopenshell.version
+ try:
+ Version(version)
+ except InvalidVersion:
+ FreeCAD.Console.PrintWarning(f"Invalid IfcOpenShell version: {version}\n")
+ version = ""
except:
version = ""
- if version.startswith("v"):
- # this is a pip version
- vendor_path = utils.get_pip_target_directory()
- result = self.run_pip(["list", "--path", vendor_path])
- if result:
- result = result.stdout.split()
- if "ifcopenshell" in result:
- version = result[result.index("ifcopenshell")+1]
+
return version
@@ -159,31 +164,6 @@ class IFC_UpdateIOS:
return None
- def compare_versions(self, v1, v2):
- """Compare two version strings in the form '0.7.0' or v0.7.0"""
-
- # code from https://www.geeksforgeeks.org/compare-two-version-numbers
-
- arr1 = v1.replace("v","").split(".")
- arr2 = v2.replace("v","").split(".")
- n = len(arr1)
- m = len(arr2)
- arr1 = [int(i) for i in arr1]
- arr2 = [int(i) for i in arr2]
- if n > m:
- for i in range(m, n):
- arr2.append(0)
- elif m > n:
- for i in range(n, m):
- arr1.append(0)
- for i in range(len(arr1)):
- if arr1[i] > arr2[i]:
- return 1
- elif arr2[i] > arr1[i]:
- return -1
- return 0
-
-
FreeCADGui.addCommand("IFC_UpdateIOS", IFC_UpdateIOS())
From 3c77bd4fcfa74c02fa65d35043e24cc457ab46ac Mon Sep 17 00:00:00 2001
From: Chris Hennes
Date: Mon, 3 Mar 2025 15:01:37 -0600
Subject: [PATCH 25/31] Addon Manager: Adjust use of QDialogButtonBox
---
src/Mod/AddonManager/update_all.ui | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/Mod/AddonManager/update_all.ui b/src/Mod/AddonManager/update_all.ui
index 266b779f9a..27672917d5 100644
--- a/src/Mod/AddonManager/update_all.ui
+++ b/src/Mod/AddonManager/update_all.ui
@@ -26,8 +26,14 @@
-
+
+ Qt::ScrollBarAlwaysOff
+
+
+ QAbstractScrollArea::AdjustToContents
+
- QAbstractItemView::EditTrigger::NoEditTriggers
+ QAbstractItemView::NoEditTriggers
false
@@ -39,10 +45,10 @@
false
- QAbstractItemView::SelectionMode::NoSelection
+ QAbstractItemView::NoSelection
- QAbstractItemView::SelectionBehavior::SelectRows
+ QAbstractItemView::SelectItems
false
@@ -60,11 +66,8 @@
-
-
- Qt::Orientation::Horizontal
-
- QDialogButtonBox::StandardButton::Cancel
+ QDialogButtonBox::Cancel
From 1ab8bed308118badce894f4e29f237ae3fbe8254 Mon Sep 17 00:00:00 2001
From: mosfet80
Date: Tue, 4 Mar 2025 23:10:12 +0100
Subject: [PATCH 26/31] clean FindOCC.cmake (#19755)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* clean FindOCC.cmake
cmake version is always major then 3.16.3
.
* Update cMake/FindOCC.cmake
Co-authored-by: Benjamin Bræstrup Sayoc
* Update cMake/FindOCC.cmake
Co-authored-by: Benjamin Bræstrup Sayoc
---------
Co-authored-by: Benjamin Bræstrup Sayoc
---
cMake/FindOCC.cmake | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/cMake/FindOCC.cmake b/cMake/FindOCC.cmake
index 21e9465282..552f0b1896 100644
--- a/cMake/FindOCC.cmake
+++ b/cMake/FindOCC.cmake
@@ -10,12 +10,10 @@
# we first try to find opencascade directly:
if (NOT OCCT_CMAKE_FALLBACK)
find_package(OpenCASCADE CONFIG QUIET)
- if (NOT (CMAKE_VERSION VERSION_LESS 3.6.0))
- get_property(flags DIRECTORY PROPERTY COMPILE_DEFINITIONS)
- # OCCT 7.5 adds this define that causes hundreds of compiler warnings with Qt5.x, so remove it again
- list(FILTER flags EXCLUDE REGEX [[GL_GLEXT_LEGACY]])
- set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS ${flags})
- endif ()
+ get_property(flags DIRECTORY PROPERTY COMPILE_DEFINITIONS)
+ # OCCT 7.5 adds this define that causes hundreds of compiler warnings with Qt5.x, so remove it again
+ list(FILTER flags EXCLUDE REGEX [[GL_GLEXT_LEGACY]])
+ set_property(DIRECTORY PROPERTY COMPILE_DEFINITIONS ${flags})
endif ()
if (OpenCASCADE_FOUND)
set(OCC_FOUND ${OpenCASCADE_FOUND})
From f8032e9ddbb3b528c573a7098c9393516a73930b Mon Sep 17 00:00:00 2001
From: Jacob Oursland
Date: Tue, 4 Mar 2025 09:49:37 -0700
Subject: [PATCH 27/31] About: Add build date to clipboard data.
---
src/Gui/Dialogs/DlgAbout.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/Gui/Dialogs/DlgAbout.cpp b/src/Gui/Dialogs/DlgAbout.cpp
index 45b253a232..357c5c39c6 100644
--- a/src/Gui/Dialogs/DlgAbout.cpp
+++ b/src/Gui/Dialogs/DlgAbout.cpp
@@ -580,6 +580,7 @@ void AboutDialog::copyToClipboard()
QString point = QString::fromStdString(config["BuildVersionPoint"]);
QString suffix = QString::fromStdString(config["BuildVersionSuffix"]);
QString build = QString::fromStdString(config["BuildRevision"]);
+ QString disda = QString::fromStdString(config["BuildRevisionDate"]);
QString deskEnv =
QProcessEnvironment::systemEnvironment().value(QStringLiteral("XDG_CURRENT_DESKTOP"),
@@ -628,6 +629,7 @@ void AboutDialog::copyToClipboard()
str << " Snap " << snap;
}
str << '\n';
+ str << "Build date: " << disda << "\n";
#if defined(_DEBUG) || defined(DEBUG)
str << "Build type: Debug\n";
From f3c6ea92d63dd88d2a57ecf7a54bf2fd8d4d767d Mon Sep 17 00:00:00 2001
From: Chris Hennes
Date: Tue, 4 Mar 2025 16:14:16 -0600
Subject: [PATCH 28/31] Apply suggestions from code review
Co-authored-by: Benjamin Nauck
---
src/Gui/Dialogs/DlgAbout.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/Gui/Dialogs/DlgAbout.cpp b/src/Gui/Dialogs/DlgAbout.cpp
index 357c5c39c6..ca6c8b9011 100644
--- a/src/Gui/Dialogs/DlgAbout.cpp
+++ b/src/Gui/Dialogs/DlgAbout.cpp
@@ -580,7 +580,7 @@ void AboutDialog::copyToClipboard()
QString point = QString::fromStdString(config["BuildVersionPoint"]);
QString suffix = QString::fromStdString(config["BuildVersionSuffix"]);
QString build = QString::fromStdString(config["BuildRevision"]);
- QString disda = QString::fromStdString(config["BuildRevisionDate"]);
+ QString buildDate = QString::fromStdString(config["BuildRevisionDate"]);
QString deskEnv =
QProcessEnvironment::systemEnvironment().value(QStringLiteral("XDG_CURRENT_DESKTOP"),
@@ -629,7 +629,7 @@ void AboutDialog::copyToClipboard()
str << " Snap " << snap;
}
str << '\n';
- str << "Build date: " << disda << "\n";
+ str << "Build date: " << buildDate << "\n";
#if defined(_DEBUG) || defined(DEBUG)
str << "Build type: Debug\n";
From 56a87cbf555df60a6b026e5d975e30580533e948 Mon Sep 17 00:00:00 2001
From: Chris Hennes
Date: Tue, 4 Mar 2025 18:28:07 -0600
Subject: [PATCH 29/31] CI: Add merge_group as trigger
Required to force CI run on merge queue
---
.github/workflows/CI_master.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/CI_master.yml b/.github/workflows/CI_master.yml
index ca74ea454c..55b2db64d7 100644
--- a/.github/workflows/CI_master.yml
+++ b/.github/workflows/CI_master.yml
@@ -26,7 +26,7 @@
name: FreeCAD master CI
-on: [workflow_dispatch, push, pull_request]
+on: [workflow_dispatch, push, pull_request, merge_group]
concurrency:
group: FC-CI-${{ github.head_ref || github.run_id }}
From 46b2ec526b434b575ef4c56bff17eba8959f9b35 Mon Sep 17 00:00:00 2001
From: Syres916 <46537884+Syres916@users.noreply.github.com>
Date: Thu, 23 Jan 2025 23:56:14 +0000
Subject: [PATCH 30/31] [Gui] Trigger the same checks for Drag & Drop files as
File > Open
---
src/Gui/Application.cpp | 30 ++++++++++++++++++++++++++++++
src/Gui/Application.h | 4 ++++
src/Gui/CommandDoc.cpp | 24 ++----------------------
3 files changed, 36 insertions(+), 22 deletions(-)
diff --git a/src/Gui/Application.cpp b/src/Gui/Application.cpp
index 5a13553bf4..c8827f4b01 100644
--- a/src/Gui/Application.cpp
+++ b/src/Gui/Application.cpp
@@ -26,6 +26,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -707,6 +708,11 @@ void Application::importFrom(const char* FileName, const char* DocName, const ch
// load the file with the module
if (File.hasExtension("FCStd")) {
Command::doCommand(Command::App, "%s.open(u\"%s\")", Module, unicodepath.c_str());
+ setStatus(UserInitiatedOpenDocument, false);
+ App::Document* doc = App::GetApplication().getActiveDocument();
+ checkPartialRestore(doc);
+ checkRestoreError(doc);
+ checkForRecomputes();
if (activeDocument()) {
activeDocument()->setModified(false);
}
@@ -1007,6 +1013,30 @@ void Application::checkForRecomputes() {
"Please check report view for more details."));
}
+void Application::checkPartialRestore(App::Document* doc)
+{
+ if (doc && doc->testStatus(App::Document::PartialRestore)) {
+ QMessageBox::critical(
+ getMainWindow(),
+ QObject::tr("Error"),
+ QObject::tr("There were errors while loading the file. Some data might have been "
+ "modified or not recovered at all. Look in the report view for more "
+ "specific information about the objects involved."));
+ }
+}
+
+void Application::checkRestoreError(App::Document* doc)
+{
+ if (doc && doc->testStatus(App::Document::RestoreError)) {
+ QMessageBox::critical(
+ getMainWindow(),
+ QObject::tr("Error"),
+ QObject::tr("There were serious errors while loading the file. Some data might have "
+ "been modified or not recovered at all. Saving the project will most "
+ "likely result in loss of data."));
+ }
+}
+
void Application::slotActiveDocument(const App::Document& Doc)
{
std::map::iterator doc = d->documents.find(&Doc);
diff --git a/src/Gui/Application.h b/src/Gui/Application.h
index e358e6008c..54f7c4dd15 100644
--- a/src/Gui/Application.h
+++ b/src/Gui/Application.h
@@ -79,6 +79,10 @@ public:
App::Document *reopen(App::Document *doc);
/// Prompt about recomputing if needed
static void checkForRecomputes();
+ /// Prompt about PartialRestore
+ void checkPartialRestore(App::Document* doc);
+ /// Prompt for Errors on open
+ void checkRestoreError(App::Document* doc);
//@}
diff --git a/src/Gui/CommandDoc.cpp b/src/Gui/CommandDoc.cpp
index 6d9274b69a..8ed8880d7e 100644
--- a/src/Gui/CommandDoc.cpp
+++ b/src/Gui/CommandDoc.cpp
@@ -96,26 +96,6 @@ StdCmdOpen::StdCmdOpen()
void StdCmdOpen::activated(int iMsg)
{
- // clang-format off
- auto checkPartialRestore = [](App::Document* doc) {
- if (doc && doc->testStatus(App::Document::PartialRestore)) {
- QMessageBox::critical(getMainWindow(), QObject::tr("Error"),
- QObject::tr("There were errors while loading the file. Some data might have been "
- "modified or not recovered at all. Look in the report view for more "
- "specific information about the objects involved."));
- }
- };
-
- auto checkRestoreError = [](App::Document* doc) {
- if (doc && doc->testStatus(App::Document::RestoreError)) {
- QMessageBox::critical(getMainWindow(), QObject::tr("Error"),
- QObject::tr("There were serious errors while loading the file. Some data might have "
- "been modified or not recovered at all. Saving the project will most "
- "likely result in loss of data."));
- }
- };
- // clang-format on
-
Q_UNUSED(iMsg);
// fill the list of registered endings
@@ -183,8 +163,8 @@ void StdCmdOpen::activated(int iMsg)
App::Document *doc = App::GetApplication().getActiveDocument();
- checkPartialRestore(doc);
- checkRestoreError(doc);
+ getGuiApplication()->checkPartialRestore(doc);
+ getGuiApplication()->checkRestoreError(doc);
}
}
}
From 27dd14174e991c4682b116caa818d0b67b0673fa Mon Sep 17 00:00:00 2001
From: Andrew Shkolik
Date: Wed, 26 Feb 2025 20:51:23 -0600
Subject: [PATCH 31/31] fix #18356 Sketcher: External reference line defaults
to geometry, but vertex does not
---
src/Mod/Sketcher/App/SketchObject.cpp | 19 ++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)
diff --git a/src/Mod/Sketcher/App/SketchObject.cpp b/src/Mod/Sketcher/App/SketchObject.cpp
index 0bfa228d53..4c9b8be7fe 100644
--- a/src/Mod/Sketcher/App/SketchObject.cpp
+++ b/src/Mod/Sketcher/App/SketchObject.cpp
@@ -366,10 +366,23 @@ void SketchObject::buildShape() {
auto egf = ExternalGeometryFacade::getFacade(geo);
if(!egf->testFlag(ExternalGeometryExtension::Defining))
continue;
+
auto indexedName = Data::IndexedName::fromConst("ExternalEdge", i-1);
- shapes.push_back(getEdge(geo, convertSubName(indexedName, false).c_str()));
- if (checkSmallEdge(shapes.back())) {
- FC_WARN("Edge too small: " << indexedName);
+
+ if (geo->isDerivedFrom()) {
+ Part::TopoShape vertex(TopoDS::Vertex(geo->toShape()));
+ if (!vertex.hasElementMap()) {
+ vertex.resetElementMap(std::make_shared());
+ }
+ vertex.setElementName(Data::IndexedName::fromConst("Vertex", 1),
+ Data::MappedName::fromRawData(convertSubName(indexedName, false).c_str()),0L);
+ vertices.push_back(vertex);
+ vertices.back().copyElementMap(vertex, Part::OpCodes::Sketch);
+ } else {
+ shapes.push_back(getEdge(geo, convertSubName(indexedName, false).c_str()));
+ if (checkSmallEdge(shapes.back())) {
+ FC_WARN("Edge too small: " << indexedName);
+ }
}
}