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.
167 lines
10 KiB
Bash
Executable File
167 lines
10 KiB
Bash
Executable File
#!/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}"
|