mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-05-11 01:58:22 +08:00
- Added 900+ deprecated alias headers in `src/Deprecated/NCollectionAliases/` providing typedef wrappers with deprecation warnings - Updated build system to install deprecated headers alongside regular headers
514 lines
18 KiB
Python
514 lines
18 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (c) 2025 OPEN CASCADE SAS
|
|
#
|
|
# This file is part of Open CASCADE Technology software library.
|
|
#
|
|
# This library is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU Lesser General Public License version 2.1 as published
|
|
# by the Free Software Foundation, with special exception defined in the file
|
|
# OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
|
# distribution for complete text of the license and disclaimer of any warranty.
|
|
#
|
|
# Alternatively, this file may be used under the terms of Open CASCADE
|
|
# commercial license or contractual agreement.
|
|
|
|
"""
|
|
OCCT 8.0.0 Deprecated Header Generator
|
|
|
|
Generates deprecated header files from collected_typedefs.json for backward compatibility.
|
|
These headers provide deprecated typedef aliases that emit compile-time warnings.
|
|
|
|
Usage:
|
|
python3 generate_deprecated_headers.py [options]
|
|
|
|
Options:
|
|
--input FILE Input JSON from collect_typedefs.py (default: collected_typedefs.json)
|
|
--output-dir DIR Output directory for deprecated headers (default: src/Deprecated)
|
|
--dry-run Preview changes without writing files
|
|
--verbose Show detailed progress
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import argparse
|
|
from pathlib import Path
|
|
from typing import Dict, List, Set, Optional
|
|
from collections import defaultdict
|
|
|
|
# OCCT license header
|
|
LICENSE_HEADER = """// Copyright (c) 2025 OPEN CASCADE SAS
|
|
//
|
|
// This file is part of Open CASCADE Technology software library.
|
|
//
|
|
// This library is free software; you can redistribute it and/or modify it under
|
|
// the terms of the GNU Lesser General Public License version 2.1 as published
|
|
// by the Free Software Foundation, with special exception defined in the file
|
|
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
|
|
// distribution for complete text of the license and disclaimer of any warranty.
|
|
//
|
|
// Alternatively, this file may be used under the terms of Open CASCADE
|
|
// commercial license or contractual agreement.
|
|
|
|
//! @file {filename}
|
|
//! @brief Deprecated typedef for backward compatibility.
|
|
//! @deprecated This header is deprecated since OCCT 8.0.0.
|
|
//! Use {replacement} directly instead.
|
|
"""
|
|
|
|
# Mapping of collection types to their NCollection headers
|
|
COLLECTION_HEADERS = {
|
|
'Array1': 'NCollection_Array1.hxx',
|
|
'Array2': 'NCollection_Array2.hxx',
|
|
'HArray1': 'NCollection_HArray1.hxx',
|
|
'HArray2': 'NCollection_HArray2.hxx',
|
|
'List': 'NCollection_List.hxx',
|
|
'Sequence': 'NCollection_Sequence.hxx',
|
|
'HSequence': 'NCollection_HSequence.hxx',
|
|
'Map': 'NCollection_Map.hxx',
|
|
'DataMap': 'NCollection_DataMap.hxx',
|
|
'IndexedMap': 'NCollection_IndexedMap.hxx',
|
|
'IndexedDataMap': 'NCollection_IndexedDataMap.hxx',
|
|
'DoubleMap': 'NCollection_DoubleMap.hxx',
|
|
'Vector': 'NCollection_Vector.hxx',
|
|
'Vec2': 'NCollection_Vec2.hxx',
|
|
'Vec3': 'NCollection_Vec3.hxx',
|
|
'Vec4': 'NCollection_Vec4.hxx',
|
|
'Mat4': 'NCollection_Mat4.hxx',
|
|
'UBTree': 'NCollection_UBTree.hxx',
|
|
'CellFilter': 'NCollection_CellFilter.hxx',
|
|
'Shared': 'NCollection_Shared.hxx',
|
|
}
|
|
|
|
|
|
def convert_occ_handle(type_str: str) -> str:
|
|
"""Convert occ::handle<T> to opencascade::handle<T>."""
|
|
return type_str.replace('occ::handle', 'opencascade::handle')
|
|
|
|
|
|
def get_package_from_typedef(typedef_name: str) -> str:
|
|
"""Extract package name from typedef name (e.g., TColStd_Array1OfInteger -> TColStd)."""
|
|
if '_' in typedef_name:
|
|
return typedef_name.split('_')[0]
|
|
return ''
|
|
|
|
|
|
def get_header_basename(header_path: str) -> str:
|
|
"""Get the basename of the header file."""
|
|
return os.path.basename(header_path)
|
|
|
|
|
|
def extract_dependencies(full_type: str, template_params: str) -> Set[str]:
|
|
"""Extract type dependencies that need to be forward-declared or included."""
|
|
deps = set()
|
|
|
|
# Extract types from template parameters
|
|
# This is a simplified extraction - may need refinement for complex cases
|
|
params = template_params.replace('occ::handle<', '').replace('>', '')
|
|
for param in params.split(','):
|
|
param = param.strip()
|
|
# Skip primitive types
|
|
if param in ('int', 'double', 'float', 'char', 'bool', 'unsigned int',
|
|
'uint8_t', 'int64_t', 'size_t', 'void*', 'Standard_Integer',
|
|
'Standard_Real', 'Standard_Boolean'):
|
|
continue
|
|
# Extract class name if it's a handle
|
|
if '::' in param and 'handle' not in param:
|
|
continue
|
|
if param and not param.startswith('NCollection_'):
|
|
deps.add(param)
|
|
|
|
return deps
|
|
|
|
|
|
# Mapping of common types to their headers for proper includes
|
|
TYPE_TO_HEADER = {
|
|
'TopoDS_Shape': 'TopoDS_Shape.hxx',
|
|
'TopoDS_Edge': 'TopoDS_Edge.hxx',
|
|
'TopoDS_Face': 'TopoDS_Face.hxx',
|
|
'TopoDS_Vertex': 'TopoDS_Vertex.hxx',
|
|
'TopoDS_Wire': 'TopoDS_Wire.hxx',
|
|
'TopoDS_Shell': 'TopoDS_Shell.hxx',
|
|
'TopoDS_Solid': 'TopoDS_Solid.hxx',
|
|
'TopoDS_Compound': 'TopoDS_Compound.hxx',
|
|
'gp_Pnt': 'gp_Pnt.hxx',
|
|
'gp_Pnt2d': 'gp_Pnt2d.hxx',
|
|
'gp_Vec': 'gp_Vec.hxx',
|
|
'gp_Vec2d': 'gp_Vec2d.hxx',
|
|
'gp_Dir': 'gp_Dir.hxx',
|
|
'gp_Dir2d': 'gp_Dir2d.hxx',
|
|
'gp_XYZ': 'gp_XYZ.hxx',
|
|
'gp_XY': 'gp_XY.hxx',
|
|
'gp_Trsf': 'gp_Trsf.hxx',
|
|
'gp_Trsf2d': 'gp_Trsf2d.hxx',
|
|
'gp_GTrsf2d': 'gp_GTrsf2d.hxx',
|
|
'gp_Ax1': 'gp_Ax1.hxx',
|
|
'gp_Ax2': 'gp_Ax2.hxx',
|
|
'gp_Ax3': 'gp_Ax3.hxx',
|
|
'gp_Lin': 'gp_Lin.hxx',
|
|
'gp_Lin2d': 'gp_Lin2d.hxx',
|
|
'gp_Circ': 'gp_Circ.hxx',
|
|
'gp_Circ2d': 'gp_Circ2d.hxx',
|
|
'gp_Pln': 'gp_Pln.hxx',
|
|
'gp_Sphere': 'gp_Sphere.hxx',
|
|
'gp_Cylinder': 'gp_Cylinder.hxx',
|
|
'gp_Cone': 'gp_Cone.hxx',
|
|
'gp_Torus': 'gp_Torus.hxx',
|
|
'TCollection_AsciiString': 'TCollection_AsciiString.hxx',
|
|
'TCollection_ExtendedString': 'TCollection_ExtendedString.hxx',
|
|
'TCollection_HAsciiString': 'TCollection_HAsciiString.hxx',
|
|
'TCollection_HExtendedString': 'TCollection_HExtendedString.hxx',
|
|
'Standard_GUID': 'Standard_GUID.hxx',
|
|
'Standard_Transient': 'Standard_Transient.hxx',
|
|
'Bnd_Box': 'Bnd_Box.hxx',
|
|
'Bnd_Box2d': 'Bnd_Box2d.hxx',
|
|
}
|
|
|
|
|
|
def get_includes_for_type(type_str: str) -> List[str]:
|
|
"""Get required includes for a type used in template parameters."""
|
|
includes = []
|
|
|
|
# Check for handles
|
|
if 'opencascade::handle' in type_str or 'occ::handle' in type_str:
|
|
includes.append('Standard_Handle.hxx')
|
|
|
|
# Extract type names and add includes
|
|
for type_name, header in TYPE_TO_HEADER.items():
|
|
if type_name in type_str:
|
|
includes.append(header)
|
|
|
|
return includes
|
|
|
|
|
|
def generate_header_content(header_file: str, typedefs: List[Dict],
|
|
package_name: str,
|
|
original_includes: Optional[List[str]] = None) -> str:
|
|
"""Generate the content of a deprecated header file.
|
|
|
|
Args:
|
|
header_file: Original header file path
|
|
typedefs: List of typedef dictionaries
|
|
package_name: Package name
|
|
original_includes: List of includes from original header (if available)
|
|
"""
|
|
filename = get_header_basename(header_file)
|
|
guard_name = f"_{filename.replace('.', '_')}"
|
|
|
|
# Collect all collection types used
|
|
collection_types = set()
|
|
all_includes = set()
|
|
|
|
for td in typedefs:
|
|
col_type = td['collection_type']
|
|
if col_type in COLLECTION_HEADERS:
|
|
collection_types.add(col_type)
|
|
|
|
# Get includes needed for template parameters (fallback when original includes not available)
|
|
if not original_includes:
|
|
full_type = convert_occ_handle(td['full_type'])
|
|
template_params = td.get('template_params', '')
|
|
type_includes = get_includes_for_type(full_type)
|
|
all_includes.update(type_includes)
|
|
|
|
# Determine replacement text for documentation
|
|
if len(typedefs) == 1:
|
|
replacement = convert_occ_handle(typedefs[0]['full_type'])
|
|
else:
|
|
replacement = "NCollection types"
|
|
|
|
# Build the header content
|
|
lines = []
|
|
|
|
# License header
|
|
lines.append(LICENSE_HEADER.format(filename=filename, replacement=replacement))
|
|
|
|
# Include guard
|
|
lines.append(f"#ifndef {guard_name}")
|
|
lines.append(f"#define {guard_name}")
|
|
lines.append("")
|
|
|
|
# All includes together - Standard_Macro first, then others
|
|
lines.append("#include <Standard_Macro.hxx>")
|
|
|
|
if original_includes:
|
|
# Use original includes from the source header (preferred)
|
|
for inc in original_includes:
|
|
# Skip the header's own potential circular include and Standard_Macro.hxx (already included)
|
|
if inc != filename and not inc.endswith(filename) and inc != 'Standard_Macro.hxx':
|
|
lines.append(f"#include <{inc}>")
|
|
else:
|
|
# Fallback: Include required NCollection headers based on typedef analysis
|
|
for col_type in sorted(collection_types):
|
|
if col_type in COLLECTION_HEADERS:
|
|
lines.append(f"#include <{COLLECTION_HEADERS[col_type]}>")
|
|
|
|
# Include type dependencies detected from template parameters
|
|
for inc in sorted(all_includes):
|
|
lines.append(f"#include <{inc}>")
|
|
|
|
lines.append("")
|
|
|
|
# Header deprecation warning (after all includes)
|
|
lines.append(f'Standard_HEADER_DEPRECATED("{filename} is deprecated since OCCT 8.0.0. '
|
|
f'Use {replacement} directly.")')
|
|
|
|
lines.append("")
|
|
|
|
# Generate deprecated typedefs
|
|
for td in typedefs:
|
|
typedef_name = td['typedef_name']
|
|
full_type = convert_occ_handle(td['full_type'])
|
|
is_iterator = td.get('is_iterator', False)
|
|
|
|
# Deprecation message
|
|
dep_msg = f"{typedef_name} is deprecated, use {full_type} directly"
|
|
|
|
# Generate the typedef with deprecation attribute (original typedef syntax)
|
|
lines.append(f'Standard_DEPRECATED("{dep_msg}")')
|
|
lines.append(f'typedef {full_type} {typedef_name};')
|
|
|
|
lines.append("")
|
|
lines.append(f"#endif // {guard_name}")
|
|
lines.append("")
|
|
|
|
return '\n'.join(lines)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='OCCT Deprecated Header Generator')
|
|
parser.add_argument('--input', '-i', default='collected_typedefs.json',
|
|
help='Input JSON file (default: collected_typedefs.json)')
|
|
parser.add_argument('--output-dir', '-o', default=None,
|
|
help='Output directory (default: src/Deprecated/NCollectionAliases)')
|
|
parser.add_argument('--dry-run', action='store_true',
|
|
help='Preview without writing files')
|
|
parser.add_argument('--verbose', '-v', action='store_true',
|
|
help='Verbose output')
|
|
|
|
args = parser.parse_args()
|
|
|
|
print("OCCT Deprecated Header Generator")
|
|
print("=" * 60)
|
|
|
|
# Determine paths
|
|
script_dir = Path(__file__).parent
|
|
input_file = script_dir / args.input if not os.path.isabs(args.input) else Path(args.input)
|
|
|
|
if args.output_dir:
|
|
output_dir = Path(args.output_dir)
|
|
else:
|
|
# Default: src/Deprecated/NCollectionAliases relative to OCCT root
|
|
occt_root = script_dir.parent.parent.parent
|
|
output_dir = occt_root / 'src' / 'Deprecated' / 'NCollectionAliases'
|
|
|
|
print(f"Input file: {input_file}")
|
|
print(f"Output directory: {output_dir}")
|
|
if args.dry_run:
|
|
print("(DRY RUN - no files will be written)")
|
|
print()
|
|
|
|
# Load JSON data
|
|
with open(input_file, 'r') as f:
|
|
data = json.load(f)
|
|
|
|
# Get typedef-only headers
|
|
typedef_only_headers = set(data.get('typedef_only_headers', []))
|
|
print(f"Found {len(typedef_only_headers)} typedef-only headers")
|
|
|
|
# Group typedefs by header file
|
|
headers_data = data.get('headers', {})
|
|
|
|
# Track statistics
|
|
headers_created = 0
|
|
typedefs_processed = 0
|
|
packages = set()
|
|
|
|
# Process each typedef-only header
|
|
for header_path in sorted(typedef_only_headers):
|
|
header_info = headers_data.get(header_path, {})
|
|
typedefs = header_info.get('typedefs', [])
|
|
|
|
if not typedefs:
|
|
continue
|
|
|
|
# Skip headers without underscore in typedef name (typically local aliases)
|
|
if all('_' not in td['typedef_name'] for td in typedefs):
|
|
if args.verbose:
|
|
print(f" Skipping {header_path} (no package-prefixed typedefs)")
|
|
continue
|
|
|
|
# Filter to only package-prefixed typedefs
|
|
package_typedefs = [td for td in typedefs if '_' in td['typedef_name']]
|
|
if not package_typedefs:
|
|
continue
|
|
|
|
# Extract package name from first typedef
|
|
package_name = get_package_from_typedef(package_typedefs[0]['typedef_name'])
|
|
packages.add(package_name)
|
|
|
|
# Determine output path - simple flat structure
|
|
filename = get_header_basename(header_path)
|
|
out_path = output_dir / filename
|
|
|
|
# Get original includes if available in JSON
|
|
original_includes = header_info.get('includes', None)
|
|
|
|
# Generate header content
|
|
content = generate_header_content(header_path, package_typedefs, package_name,
|
|
original_includes)
|
|
|
|
if args.verbose:
|
|
print(f" {header_path} -> {filename}")
|
|
|
|
if not args.dry_run:
|
|
out_path.parent.mkdir(parents=True, exist_ok=True)
|
|
with open(out_path, 'w') as f:
|
|
f.write(content)
|
|
|
|
headers_created += 1
|
|
typedefs_processed += len(package_typedefs)
|
|
|
|
# Summary
|
|
print()
|
|
print("=" * 60)
|
|
print("SUMMARY")
|
|
print("=" * 60)
|
|
print(f"Headers created: {headers_created}")
|
|
print(f"Typedefs processed: {typedefs_processed}")
|
|
print(f"Original packages: {len(packages)}")
|
|
print(f"Output directory: {output_dir}")
|
|
|
|
if args.verbose:
|
|
print("\nOriginal packages:")
|
|
for pkg in sorted(packages):
|
|
print(f" {pkg}")
|
|
|
|
print("\nDone!")
|
|
print("Headers are ready to be installed via CMakeLists.txt")
|
|
if args.dry_run:
|
|
print("Run without --dry-run to create files")
|
|
|
|
|
|
def generate_flat_cmake_files(output_dir: Path, all_files: List[str], verbose: bool):
|
|
"""Generate FILES.cmake for flat structure (single Deprecated package)."""
|
|
cmake_dir = output_dir / 'TKDeprecated' / 'Deprecated'
|
|
cmake_file = cmake_dir / 'FILES.cmake'
|
|
|
|
content = """# Auto-generated deprecated typedef headers
|
|
# These headers provide backward compatibility aliases for OCCT 8.0.0
|
|
set(OCCT_Deprecated_FILES
|
|
"""
|
|
for f in sorted(all_files):
|
|
content += f" {f}\n"
|
|
content += ")\n"
|
|
|
|
cmake_dir.mkdir(parents=True, exist_ok=True)
|
|
with open(cmake_file, 'w') as f:
|
|
f.write(content)
|
|
|
|
if verbose:
|
|
print(f" Created {cmake_file.relative_to(output_dir)}")
|
|
|
|
|
|
def generate_cmake_files(output_dir: Path, packages: Set[str],
|
|
typedef_only_headers: Set[str], headers_data: Dict,
|
|
verbose: bool):
|
|
"""Generate FILES.cmake for each package."""
|
|
# Group headers by package
|
|
package_files = defaultdict(list)
|
|
|
|
for header_path in sorted(typedef_only_headers):
|
|
header_info = headers_data.get(header_path, {})
|
|
typedefs = header_info.get('typedefs', [])
|
|
|
|
if not typedefs:
|
|
continue
|
|
|
|
# Get package-prefixed typedefs
|
|
package_typedefs = [td for td in typedefs if '_' in td['typedef_name']]
|
|
if not package_typedefs:
|
|
continue
|
|
|
|
package_name = get_package_from_typedef(package_typedefs[0]['typedef_name'])
|
|
filename = get_header_basename(header_path)
|
|
package_files[package_name].append(filename)
|
|
|
|
# Write FILES.cmake for each package
|
|
for package_name, files in sorted(package_files.items()):
|
|
cmake_dir = output_dir / 'TKDeprecated' / package_name
|
|
cmake_file = cmake_dir / 'FILES.cmake'
|
|
|
|
content = f"""# Auto-generated deprecated typedef headers for {package_name}
|
|
set(OCCT_{package_name}_FILES_Deprecated
|
|
"""
|
|
for f in sorted(files):
|
|
content += f" {f}\n"
|
|
content += ")\n"
|
|
|
|
cmake_dir.mkdir(parents=True, exist_ok=True)
|
|
with open(cmake_file, 'w') as f:
|
|
f.write(content)
|
|
|
|
if verbose:
|
|
print(f" Created {cmake_file.relative_to(output_dir)}")
|
|
|
|
|
|
def generate_module_structure(output_dir: Path, packages: Set[str], verbose: bool,
|
|
flat: bool = False):
|
|
"""Generate MODULES.cmake, TOOLKITS.cmake, PACKAGES.cmake structure."""
|
|
deprecated_dir = output_dir
|
|
tk_dir = deprecated_dir / 'TKDeprecated'
|
|
|
|
# Create TOOLKITS.cmake for Deprecated module
|
|
toolkits_file = deprecated_dir / 'TOOLKITS.cmake'
|
|
toolkits_content = """# Deprecated module - backward compatibility typedefs
|
|
# This module provides deprecated typedef headers for backward compatibility.
|
|
# All headers emit deprecation warnings when included.
|
|
set(Deprecated_TOOLKITS
|
|
TKDeprecated
|
|
)
|
|
"""
|
|
with open(toolkits_file, 'w') as f:
|
|
f.write(toolkits_content)
|
|
if verbose:
|
|
print(f" Created TOOLKITS.cmake")
|
|
|
|
# Create PACKAGES.cmake for TKDeprecated toolkit
|
|
packages_file = tk_dir / 'PACKAGES.cmake'
|
|
|
|
if flat:
|
|
packages_content = """# Packages in TKDeprecated toolkit
|
|
# Using flat structure with single Deprecated package
|
|
set(TKDeprecated_PACKAGES
|
|
Deprecated
|
|
)
|
|
"""
|
|
else:
|
|
packages_content = "# Packages in TKDeprecated toolkit\nset(TKDeprecated_PACKAGES\n"
|
|
for pkg in sorted(packages):
|
|
packages_content += f" {pkg}\n"
|
|
packages_content += ")\n"
|
|
|
|
tk_dir.mkdir(parents=True, exist_ok=True)
|
|
with open(packages_file, 'w') as f:
|
|
f.write(packages_content)
|
|
if verbose:
|
|
print(f" Created TKDeprecated/PACKAGES.cmake")
|
|
|
|
# Create EXTERNLIB.cmake
|
|
externlib_file = tk_dir / 'EXTERNLIB.cmake'
|
|
externlib_content = """# External library dependencies for TKDeprecated
|
|
# This toolkit depends on TKernel for Standard_Macro.hxx
|
|
set(TKDeprecated_EXTERNLIB
|
|
TKernel
|
|
)
|
|
"""
|
|
with open(externlib_file, 'w') as f:
|
|
f.write(externlib_content)
|
|
if verbose:
|
|
print(f" Created TKDeprecated/EXTERNLIB.cmake")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|