feat: add candidate FreeCAD naming SDK and attachment oracles
Build and verify the candidate-only FreeCAD naming bridge and OCCT worker path, including three-stage StringHasher restoration. Add Datum, ShapeBinder, attachment-mode, and PartDesign structure oracles plus offline SDK build plans and CI boundary checks.
This commit is contained in:
166
scripts/prepare-freecad-occt8-overlay.sh
Executable file
166
scripts/prepare-freecad-occt8-overlay.sh
Executable file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
FREECAD_SOURCE_DIR="${ROOT_DIR}/.cache/freecad/FreeCAD"
|
||||
OVERLAY_DIR="${ROOT_DIR}/.cache/toolchains/freecad-naming-sdk/source-occt8"
|
||||
OCCT_MIGRATION="${ROOT_DIR}/.cache/occt/occt/adm/scripts/migration_800/migrate_raise_to_throw.py"
|
||||
OCCT_TYPEDEF_MIGRATION="${ROOT_DIR}/.cache/occt/occt/adm/scripts/migration_800/replace_typedefs.py"
|
||||
OCCT_TYPEDEF_MAP="${ROOT_DIR}/.cache/occt/occt/adm/scripts/migration_800/collected_typedefs.json"
|
||||
OCCT_TYPEDEF_RESULTS="adm/scripts/migration_800/replacement_results.json"
|
||||
LOCKED_COMMIT="0108fd4b4850cc46e625b60e53cea7a7bbe69f8d"
|
||||
|
||||
[[ -f "${FREECAD_SOURCE_DIR}/CMakeLists.txt" ]] || { echo "FreeCAD source is missing: ${FREECAD_SOURCE_DIR}" >&2; exit 1; }
|
||||
[[ -f "${OCCT_MIGRATION}" ]] || { echo "OCCT 8 migration script is missing: ${OCCT_MIGRATION}" >&2; exit 1; }
|
||||
[[ -f "${OCCT_TYPEDEF_MIGRATION}" && -f "${OCCT_TYPEDEF_MAP}" ]] || { echo "OCCT 8 typedef migration inputs are missing" >&2; exit 1; }
|
||||
git -C "${ROOT_DIR}/.cache/occt/occt" diff --quiet -- "${OCCT_TYPEDEF_RESULTS}" || { echo "OCCT migration result file is already modified" >&2; exit 1; }
|
||||
cleanup_migration_result() {
|
||||
git -C "${ROOT_DIR}/.cache/occt/occt" restore -- "${OCCT_TYPEDEF_RESULTS}"
|
||||
}
|
||||
trap cleanup_migration_result EXIT
|
||||
[[ "$(git -C "${FREECAD_SOURCE_DIR}" rev-parse HEAD)" == "${LOCKED_COMMIT}" ]] || { echo "FreeCAD source commit is not locked" >&2; exit 1; }
|
||||
git -C "${FREECAD_SOURCE_DIR}" diff --quiet
|
||||
git -C "${FREECAD_SOURCE_DIR}" diff --cached --quiet
|
||||
|
||||
if [[ -e "${OVERLAY_DIR}" && ! -f "${OVERLAY_DIR}/.git" ]]; then
|
||||
echo "Refusing to replace non-worktree overlay: ${OVERLAY_DIR}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -f "${OVERLAY_DIR}/.git" ]]; then
|
||||
git -C "${FREECAD_SOURCE_DIR}" worktree remove --force "${OVERLAY_DIR}"
|
||||
fi
|
||||
git -C "${FREECAD_SOURCE_DIR}" worktree prune
|
||||
git -C "${FREECAD_SOURCE_DIR}" worktree add --detach "${OVERLAY_DIR}" "${LOCKED_COMMIT}"
|
||||
|
||||
# The OCCT 8 script locates its own source root, so import it as a module and
|
||||
# apply the documented Raise-to-throw phase only to FreeCAD's source overlay.
|
||||
PYTHONDONTWRITEBYTECODE=1 python3 - "${OCCT_MIGRATION}" "${OVERLAY_DIR}/src" <<'PY'
|
||||
import importlib.util
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
script = pathlib.Path(sys.argv[1])
|
||||
source = pathlib.Path(sys.argv[2])
|
||||
spec = importlib.util.spec_from_file_location("occt8_raise_migration", script)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
files = module.find_files_with_raise(source)
|
||||
for path in files:
|
||||
module.process_file(path)
|
||||
print(f"Applied OCCT 8 Raise-to-throw migration to {len(files)} FreeCAD source files")
|
||||
PY
|
||||
PYTHONDONTWRITEBYTECODE=1 python3 "${OCCT_TYPEDEF_MIGRATION}" \
|
||||
"${OVERLAY_DIR}/src" --input "${OCCT_TYPEDEF_MAP}" --jobs "${JOBS:-4}"
|
||||
# replace_typedefs.py always writes a diagnostic beside the pinned tool. Its
|
||||
# content is not an SDK input, so restore it immediately after the overlay run.
|
||||
cleanup_migration_result
|
||||
|
||||
# OCCT 8 moved iterator typedefs into their collection headers. The official
|
||||
# typedef migration replaces usages but intentionally leaves include-only
|
||||
# references untouched, so map the removed OCCT 7 header names explicitly.
|
||||
while IFS='|' read -r removed_header collection_header; do
|
||||
mapfile -d '' include_files < <(
|
||||
rg -l -0 -F "#include <${removed_header}>" "${OVERLAY_DIR}/src" || true
|
||||
)
|
||||
if (( ${#include_files[@]} > 0 )); then
|
||||
sed -i "s|#include <${removed_header}>|#include <${collection_header}>|g" "${include_files[@]}"
|
||||
fi
|
||||
done <<'EOF'
|
||||
BRepCheck_ListIteratorOfListOfStatus.hxx|BRepCheck_ListOfStatus.hxx
|
||||
TColStd_ListIteratorOfListOfTransient.hxx|TColStd_ListOfTransient.hxx
|
||||
TColStd_MapIteratorOfMapOfTransient.hxx|TColStd_MapOfTransient.hxx
|
||||
TopTools_DataMapIteratorOfDataMapOfIntegerListOfShape.hxx|TopTools_DataMapOfIntegerListOfShape.hxx
|
||||
TopTools_DataMapIteratorOfDataMapOfShapeShape.hxx|TopTools_DataMapOfShapeShape.hxx
|
||||
TopTools_ListIteratorOfListOfShape.hxx|TopTools_ListOfShape.hxx
|
||||
EOF
|
||||
|
||||
# Standard_Failure no longer participates in OCCT RTTI. OCCT 8 documents
|
||||
# ExceptionType() as the source-compatible exception class-name API.
|
||||
while IFS='|' read -r legacy_expression replacement_expression; do
|
||||
mapfile -d '' exception_files < <(
|
||||
rg -l -0 -F "${legacy_expression}" "${OVERLAY_DIR}/src" || true
|
||||
)
|
||||
if (( ${#exception_files[@]} > 0 )); then
|
||||
sed -i "s|${legacy_expression}|${replacement_expression}|g" "${exception_files[@]}"
|
||||
fi
|
||||
done <<'EOF'
|
||||
e.DynamicType()->get_type_name()|e.ExceptionType()
|
||||
e.DynamicType()->Name()|e.ExceptionType()
|
||||
ex.DynamicType()->Name()|ex.ExceptionType()
|
||||
EOF
|
||||
|
||||
# BRepAdaptor_Curve exposes these inherited parameter accessors directly in
|
||||
# OCCT 8; the removed BRepLProp_CurveTool only forwarded these calls here.
|
||||
while IFS='|' read -r legacy_expression replacement_expression; do
|
||||
mapfile -d '' curve_tool_files < <(
|
||||
rg -l -0 -F "${legacy_expression}" "${OVERLAY_DIR}/src" || true
|
||||
)
|
||||
if (( ${#curve_tool_files[@]} > 0 )); then
|
||||
sed -i "s|${legacy_expression}|${replacement_expression}|g" "${curve_tool_files[@]}"
|
||||
fi
|
||||
done <<'EOF'
|
||||
BRepLProp_CurveTool::FirstParameter(adapt)|adapt.FirstParameter()
|
||||
BRepLProp_CurveTool::LastParameter(adapt)|adapt.LastParameter()
|
||||
EOF
|
||||
mapfile -d '' curve_tool_include_files < <(
|
||||
rg -l -0 '^[[:space:]]*#[[:space:]]*include[[:space:]]*<BRepLProp_CurveTool\.hxx>' \
|
||||
"${OVERLAY_DIR}/src" || true
|
||||
)
|
||||
if (( ${#curve_tool_include_files[@]} > 0 )); then
|
||||
sed -i -E '\|^[[:space:]]*#[[:space:]]*include[[:space:]]*<BRepLProp_CurveTool\.hxx>|d' \
|
||||
"${curve_tool_include_files[@]}"
|
||||
fi
|
||||
|
||||
# OCCT 8 consolidates Geom2dLProp into GeomLProp while preserving the 2D
|
||||
# specialization's constructor and methods under the unified class name.
|
||||
mapfile -d '' geom2d_lprop_files < <(
|
||||
rg -l -0 -F 'Geom2dLProp_CLProps2d' "${OVERLAY_DIR}/src" || true
|
||||
)
|
||||
if (( ${#geom2d_lprop_files[@]} > 0 )); then
|
||||
sed -i \
|
||||
-e 's|#include <Geom2dLProp_CLProps2d.hxx>|#include <GeomLProp_CLProps.hxx>|g' \
|
||||
-e 's|\bGeom2dLProp_CLProps2d\b|GeomLProp_CLProps2d|g' \
|
||||
"${geom2d_lprop_files[@]}"
|
||||
fi
|
||||
|
||||
# OCCT 8 adds an initializer-list constructor to NCollection_List, making
|
||||
# these calls ambiguous with FreeCAD's std::vector<TopoShape> overload. The
|
||||
# latter was the only viable overload with the pinned FreeCAD/OCCT 7 sources.
|
||||
TOPO_SHAPE_EXPANSION="${OVERLAY_DIR}/src/Mod/Part/App/TopoShapeExpansion.cpp"
|
||||
sed -i \
|
||||
-e 's|mapper.populate(MappingStatus::Modified, e, {e1, e2, e3, e4});|mapper.populate(MappingStatus::Modified, e, std::vector<TopoShape>{e1, e2, e3, e4});|' \
|
||||
-e 's|mapper.populate(MappingStatus::Generated, v, {TopExp::FirstVertex(e1)});|mapper.populate(MappingStatus::Generated, v, std::vector<TopoShape>{TopExp::FirstVertex(e1)});|' \
|
||||
-e 's|mapper.populate(MappingStatus::Generated, v, {TopExp::LastVertex(e4)});|mapper.populate(MappingStatus::Generated, v, std::vector<TopoShape>{TopExp::LastVertex(e4)});|' \
|
||||
"${TOPO_SHAPE_EXPANSION}"
|
||||
|
||||
# Standard_Failure inherits std::exception in OCCT 8 and what() returns the
|
||||
# same message as the deprecated GetMessageString(). Remove the three now
|
||||
# unreachable duplicate fallbacks that immediately follow an identical
|
||||
# std::exception handler.
|
||||
TOPO_SHAPE_PY="${OVERLAY_DIR}/src/Mod/Part/App/TopoShapePyImp.cpp"
|
||||
perl -0pi -e '
|
||||
s|( catch \(const std::exception& e\) \{\n PyErr_SetString\(PartExceptionOCCError, e\.what\(\)\);\n return nullptr;\n \})\n catch \(Standard_Failure& e\) \{\n(?:\n)? PyErr_SetString\(PartExceptionOCCError, e\.GetMessageString\(\)\);\n return nullptr;\n \}|$1|g
|
||||
' "${TOPO_SHAPE_PY}"
|
||||
|
||||
if git -C "${OVERLAY_DIR}" status --porcelain | awk '{print $2}' | grep -Ev '^src/' >/dev/null; then
|
||||
echo "OCCT 8 migration changed files outside the FreeCAD src directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
remaining="$( (rg -l 'Standard_[A-Za-z0-9_]+::Raise\s*\(' "${OVERLAY_DIR}/src" -g '*.{h,hpp,hxx,c,cpp,cxx,lxx,pxx}' || true) | wc -l)"
|
||||
[[ "${remaining}" == "0" ]] || { echo "OCCT 8 migration left ${remaining} source files with legacy Raise calls" >&2; exit 1; }
|
||||
removed_iterator_headers="$( (rg -l '#include <(BRepCheck_ListIteratorOfListOfStatus|TColStd_ListIteratorOfListOfTransient|TColStd_MapIteratorOfMapOfTransient|TopTools_DataMapIteratorOfDataMapOfIntegerListOfShape|TopTools_DataMapIteratorOfDataMapOfShapeShape|TopTools_ListIteratorOfListOfShape)\.hxx>' "${OVERLAY_DIR}/src" || true) | wc -l)"
|
||||
[[ "${removed_iterator_headers}" == "0" ]] || { echo "OCCT 8 migration left ${removed_iterator_headers} files with removed iterator headers" >&2; exit 1; }
|
||||
legacy_exception_rtti="$( (rg -l '\b(e|ex)\.DynamicType\(\)->(Name|get_type_name)\(\)' "${OVERLAY_DIR}/src" || true) | wc -l)"
|
||||
[[ "${legacy_exception_rtti}" == "0" ]] || { echo "OCCT 8 migration left ${legacy_exception_rtti} files with legacy exception RTTI" >&2; exit 1; }
|
||||
legacy_curve_tool="$( (rg -l '\bBRepLProp_CurveTool\b' "${OVERLAY_DIR}/src" || true) | wc -l)"
|
||||
[[ "${legacy_curve_tool}" == "0" ]] || { echo "OCCT 8 migration left ${legacy_curve_tool} files using removed BRepLProp_CurveTool" >&2; exit 1; }
|
||||
legacy_geom2d_lprop="$( (rg -l '\bGeom2dLProp_CLProps2d\b' "${OVERLAY_DIR}/src" || true) | wc -l)"
|
||||
[[ "${legacy_geom2d_lprop}" == "0" ]] || { echo "OCCT 8 migration left ${legacy_geom2d_lprop} files using removed Geom2dLProp_CLProps2d" >&2; exit 1; }
|
||||
ambiguous_shape_mapper_calls="$( (rg -n 'mapper\.populate\(MappingStatus::(Modified|Generated), (e|v), \{' "${TOPO_SHAPE_EXPANSION}" || true) | wc -l)"
|
||||
[[ "${ambiguous_shape_mapper_calls}" == "0" ]] || { echo "OCCT 8 migration left ${ambiguous_shape_mapper_calls} ambiguous ShapeMapper calls" >&2; exit 1; }
|
||||
unreachable_occt_exception_fallbacks="$( (rg -U -n 'catch \(const std::exception& e\) \{\n PyErr_SetString\(PartExceptionOCCError, e\.what\(\)\);\n return nullptr;\n \}\n catch \(Standard_Failure& e\)' "${TOPO_SHAPE_PY}" || true) | wc -l)"
|
||||
[[ "${unreachable_occt_exception_fallbacks}" == "0" ]] || { echo "OCCT 8 migration left ${unreachable_occt_exception_fallbacks} unreachable Standard_Failure handlers" >&2; exit 1; }
|
||||
|
||||
git -C "${FREECAD_SOURCE_DIR}" diff --quiet
|
||||
git -C "${FREECAD_SOURCE_DIR}" diff --cached --quiet
|
||||
echo "FreeCAD OCCT 8 candidate overlay ready: ${OVERLAY_DIR}"
|
||||
Reference in New Issue
Block a user