diff --git a/compile_all.py b/compile_all.py index 040afc5..e1f84da 100644 --- a/compile_all.py +++ b/compile_all.py @@ -28,6 +28,7 @@ name.lower() for name in ( # Direct C/C++/Fortran/Rust extensions with no pure-Python distribution. + "av", "cmake", "cog", "ifcopenshell", @@ -75,6 +76,9 @@ "pydantic_core", "watchfiles", "lxml", + # Force pure-Python mypy (its wheels are mypyc-compiled, won't load under python_d). + # Keep the pin at 1.18.2; newer mypy needs the compiled-only librt.Let's w + "mypy", ) # Sitecustomize shim installed at /bin/Lib/site-packages/sitecustomize.py for @@ -297,10 +301,9 @@ def __init__( self.mode = mode self.strict_mode = True - # Right now there are two packages where the version number gets coded into the path when: Boost and Coin: - # store those two separately from all the other paths we have to track + # Boost is the one package where the version number gets coded into the path, so store + # that path separately from all the other paths we have to track self.boost_include_path = None - self.coin_cmake_path = None def get_cmake_options(self) -> List[str]: """Get a comprehensive list of cMake options that can be used in any cMake build. Not all options apply @@ -337,7 +340,6 @@ def get_cmake_options(self) -> List[str]: f"-D HDF5_DIFF_EXECUTABLE={self.install_dir}/bin/hdf5diff" + to_exe(), f"-D INSTALL_DIR={self.install_dir}", f"-D PCRE2_LIBRARY={pcre_lib}", - "-D PIVY_USE_QT6=Yes", f"-D pybind11_DIR={self.install_dir}/share/cmake/pybind11", f"-D Python_ROOT_DIR={self.install_dir}/bin", f"-D Python_DIR={self.install_dir}/bin", @@ -350,8 +352,6 @@ def get_cmake_options(self) -> List[str]: f"-D Qt6_DIR={self.install_dir}/lib/cmake/Qt6", f"-D SWIG_EXECUTABLE={self.install_dir}/bin/swig" + to_exe(), f"-D ZLIB_DIR={self.install_dir}/lib/cmake/", - "-D CMAKE_DISABLE_FIND_PACKAGE_SoQt=True", - # Absolutely never find SoQt (it's deprecated and we don't want it!) ] if self.mode == BuildMode.DEBUG: python_lib = self._python_lib_path() @@ -372,8 +372,6 @@ def get_cmake_options(self) -> List[str]: ) if self.boost_include_path: base.append(f"-D Boost_INCLUDE_DIR={self.boost_include_path}") - if self.coin_cmake_path: - base.append(f"-D Coin_DIR={self.coin_cmake_path}") if sys.platform.startswith("win32"): inc_path = self.install_dir.replace("\\", "/") cxx_flags = ( @@ -711,6 +709,14 @@ def build_python(self, args=None): "." ) + # We ship the full stdlib in Lib/, but Windows Python auto-adds + # /python.zip to sys.path ahead of Lib/ whenever that file + # exists. A stale or partial such zip (for example one left in an incremental + # build tree) would shadow Lib/ and break stdlib imports, so remove it. + stdlib_zip = os.path.join(bin_dir, f"python{major}{minor}.zip") + if os.path.exists(stdlib_zip): + os.remove(stdlib_zip) + # Construct the list of files we expect to exist that need to be placed in the toplevel directory, or in # libs: move_to_bin = ["vcruntime"] @@ -1168,6 +1174,7 @@ def build_qt(self, options: dict): "-submodules", ",".join(submodules), "-feature-opengl", + "-feature-zstd", "-prefix", self.install_dir, "-opengl", @@ -1175,6 +1182,14 @@ def build_qt(self, options: dict): ] if self.mode == BuildMode.DEBUG: init_command.append("-debug") + # Qt 6.11 has no bundled zstd, so WrapZSTD must locate the LibPack's own copy via its + # installed CMake config package. Point find_package(zstd CONFIG) directly at it. + # Requesting -feature-zstd above turns a missing zstd into a hard configure error rather + # than a silently disabled feature that would leave rcc unable to handle zstd resources. + init_command += [ + "--", + f"-Dzstd_DIR={self.install_dir}/lib/cmake/zstd".replace("\\", "/"), + ] try: self._run_streaming(init_command, "configure_log.txt") except subprocess.CalledProcessError as e: @@ -1401,41 +1416,6 @@ def _build_with_pip(self, options: dict): exit(1) self._pip_install(options["pip-install"]) - def build_coin(self, _=None): - """Builds and installs Coin using standard CMake settings""" - if self.skip_existing: - self._configure_coin_cmake_path() - if self.coin_cmake_path is not None: - print(" Not rebuilding Coin, it is already in the LibPack") - return - extra_args = ["-D COIN_BUILD_TESTS=Off"] - self._build_standard_cmake(extra_args) - self._configure_coin_cmake_path() - - def _configure_coin_cmake_path(self): - """Coin installs its cMake file into a directory named with the full version, so figure out what that is""" - start_crawl_at = os.path.join(self.install_dir, "lib", "cmake") - contents = [ - f for f in os.listdir(start_crawl_at) if os.path.isdir(os.path.join(start_crawl_at, f)) - ] - for item in contents: - if item.startswith("Coin"): - self.coin_cmake_path = os.path.join(start_crawl_at, item) - break - - def build_quarter(self, _=None): - """Builds and installs Quarter using standard CMake settings""" - if self.skip_existing: - if os.path.exists(os.path.join(self.install_dir, "include", "Quarter")): - print(" Not rebuilding Quarter, it is already in the LibPack") - return - extra_args = [ - "-D QUARTER_BUILD_EXAMPLES=Off", - "-D QUARTER_USE_QT5=Off", - "-D QUARTER_USE_QT6=On", - ] - self._build_standard_cmake(extra_args=extra_args) - def build_zlib(self, _=None): if self.skip_existing: if os.path.exists(os.path.join(self.install_dir, "include", "zlib.h")): @@ -1480,6 +1460,23 @@ def build_bzip2(self, _=None): else: raise NotImplemented("Non-Windows compilation of bzip2 is not implemented yet") + def build_expat(self, _=None): + if self.skip_existing: + if os.path.exists(os.path.join(self.install_dir, "include", "expat.h")): + print(" Not rebuilding expat, it is already in the LibPack") + return + # libexpat's CMake project lives in the expat/ subdirectory of the repository rather + # than at its root, so descend into it before running the standard CMake build. + os.chdir("expat") + extra_args = [ + "-D EXPAT_BUILD_TOOLS=OFF", + "-D EXPAT_BUILD_EXAMPLES=OFF", + "-D EXPAT_BUILD_TESTS=OFF", + "-D EXPAT_BUILD_DOCS=OFF", + "-D EXPAT_SHARED_LIBS=ON", + ] + self._build_standard_cmake(extra_args) + def build_pcre2(self, _=None): if self.skip_existing: if os.path.exists(os.path.join(self.install_dir, "include", "pcre2.h")): @@ -1494,19 +1491,6 @@ def build_swig(self, _=None): return self._build_standard_cmake() - def build_pivy(self, _=None): - if self.skip_existing: - if os.path.exists( - os.path.join(self.install_dir, "bin", "Lib", "site-packages", "pivy") - ): - print(" Not rebuilding pivy, it is already in the LibPack") - return - extra_args = [] - self._build_standard_cmake(extra_args) - if self.mode == BuildMode.DEBUG: - base = os.path.join(self.install_dir, "bin", "Lib", "site-packages", "pivy") - os.rename(os.path.join(base, "_coin.pyd"), os.path.join(base, "_coin_d.pyd")) - def build_libclang(self, _=None): """libclang is provided as a platform-specific download by Qt.""" if self.skip_existing: @@ -1611,6 +1595,22 @@ def build_libpng(self, _=None): return self._build_standard_cmake() + def build_zstd(self, _=None): + if self.skip_existing: + if os.path.exists(os.path.join(self.install_dir, "include", "zstd.h")): + print(" Not rebuilding zstd, it is already in the LibPack") + return + # Zstandard's CMake project lives in the build/cmake subdirectory of the repository + # rather than at its root, so descend into it before running the standard CMake build. + os.chdir(os.path.join("build", "cmake")) + extra_args = [ + "-D ZSTD_BUILD_SHARED=ON", + "-D ZSTD_BUILD_STATIC=ON", + "-D ZSTD_BUILD_PROGRAMS=OFF", + "-D ZSTD_BUILD_TESTS=OFF", + ] + self._build_standard_cmake(extra_args) + def build_pybind11(self, _=None): if self.skip_existing: if os.path.exists(os.path.join(self.install_dir, "include", "pybind11")): @@ -2035,7 +2035,9 @@ def build_libfmt(self, _: None): if os.path.exists(os.path.join(self.install_dir, "include", "fmt")): print(" Not rebuilding libfmt, it is already in the LibPack") return - extra_args = ["-D FMT_TEST=OFF", "-D FMT_DOC=OFF"] + # fmt 12.2.0 auto-enables FMT_MODULE under C++20/MSVC, exporting a broken fmt::fmt-module + # target that breaks find_package(fmt). FreeCAD uses fmt as a plain library, so disable it. + extra_args = ["-D FMT_TEST=OFF", "-D FMT_DOC=OFF", "-D FMT_MODULE=OFF"] self._build_standard_cmake(extra_args) def build_eigen3(self, _: None): @@ -2076,7 +2078,7 @@ def build_opencamlib(self, _: None): # ("opencamlib") under CMAKE_INSTALL_PREFIX, expecting either scikit-build to # supply a wheel root or the build driver to point CMAKE_INSTALL_PREFIX at a # site-packages directory (see src/pythonlib/pythonlib.cmake). Other Python - # C-extensions in this LibPack (for example pivy) detect Python_SITEARCH from + # C-extensions in this LibPack detect Python_SITEARCH from # CMake's FindPython and install themselves into site-packages directly; # opencamlib does not. To ensure it ends up in the right place we override the prefix # for this one package so its "opencamlib" destination resolves under the diff --git a/config.json b/config.json index bb47035..16c7caf 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "FreeCAD-version":"26.3.0", - "LibPack-version":"3.5.2", + "LibPack-version":"3.5.3", "content": [ { "name":"libiconv", @@ -45,18 +45,25 @@ "git-repo":"https://github.com/glennrp/libpng", "git-ref":"v1.6.58" }, + { + "name":"zstd", + "git-repo":"https://github.com/facebook/zstd", + "git-ref":"v1.5.7", + "note":"Zstandard. Qt 6.11 ships no bundled zstd; WrapZSTD requires an external one. Without it Qt configures with the zstd feature off, so the resource compiler (rcc) cannot produce or read zstd-compressed .qrc/.rcc resources, which breaks FreeCAD builds that use zstd resource compression. Built before Qt so build_qt can require FEATURE_zstd and fail loudly if it is ever missing again." + }, { "name":"python", "git-repo":"https://github.com/python/cpython.git", - "git-ref":"v3.14.5", + "git-ref":"v3.14.6", "requirements": [ "annotated-types==0.7.0", - "anyio==4.13.0", + "anyio==4.14.1", "attrs==23.2.0", - "certifi==2026.5.20", + "av==18.0.0", + "certifi==2026.6.17", "charset-normalizer==3.4.7", - "click==8.4.1", - "cmake==4.3.2", + "click==8.4.2", + "cmake==4.3.4", "cog==0.16.12", "colorama==0.4.6", "configparser==7.2.0", @@ -65,7 +72,7 @@ "debugpy==1.8.21", "definitions==0.2.0", "defusedxml==0.7.1", - "elementpath==5.1.2", + "elementpath==5.1.3", "fastapi==0.118.3", "fonttools==4.63.0", "h11==0.16.0", @@ -78,24 +85,25 @@ "lark==1.3.1", "lxml==6.1.1", "matplotlib==3.11.0", + "mypy==1.18.2", "nltk==3.9.4", - "numpy==2.4.6", + "numpy==2.5.0", "packaging==26.2", - "pillow==12.2.0", + "pillow==12.3.0", "ply==3.11", "pycollada==0.9.3", "pydantic==2.13.4", "pydantic_core==2.46.4", "pyparsing==3.3.2", - "pyshp==3.0.12", + "pyshp==3.1.4", "pysolar==0.13", "python-dateutil==2.9.0.post0", "python-dotenv==1.2.2", "PyYAML==6.0.3", - "regex==2026.5.9", + "regex==2026.6.28", "requests==2.34.2", "rpdb2==2.0.0.1.2", - "scipy==1.17.1", + "scipy==1.18.0", "sets==0.3.2", "setuptools==82.0.1", "shapely==2.1.2; platform_machine != \"ARM64\"", @@ -103,16 +111,16 @@ "sniffio==1.3.1", "starlette==0.48.0", "structlog==24.4.0", - "tqdm==4.68.2", + "tqdm==4.68.3", "typing-inspection==0.4.2", - "typing_extensions==4.15.0", + "typing_extensions==4.16.0", "urllib3==2.7.0", "uvicorn==0.49.0", "vermin==1.8.0", "watchfiles==1.2.0", "websockets==16.0", "wheel==0.47.0", - "xmlschema==4.3.1" + "xmlschema==4.3.2" ] }, { @@ -142,17 +150,10 @@ "git-ref": "boost-1.91.0" }, { - "name":"coin", - "git-repo":"https://github.com/coin3d/coin", - "git-ref": "v4.0.8", - "patches": ["patches/coin-01-cxx20-shared-ptr-unique.patch"], - "note": "That patch will not be needed in the next release of Coin, it's fixed upstream already" - }, - { - "name":"quarter", - "git-repo":"https://github.com/coin3d/quarter", - "git-ref":"v1.2.3", - "patches":["patches/quarter-01-add-QOpenGLContext-to-QuarterWidgetP.patch"] + "name": "expat", + "git-repo": "https://github.com/libexpat/libexpat", + "git-ref": "R_2_8_2", + "note": "Required by FreeCAD's bundled Coin, which is forced to USE_EXTERNAL_EXPAT (see cMake/FreeCAD_Helpers/SetupCoin3D.cmake)" }, { "name":"pcre2", @@ -164,16 +165,11 @@ "git-repo":"https://github.com/swig/swig.git", "git-tag":"v4.4.1" }, - { - "name":"pivy", - "git-repo":"https://github.com/coin3d/pivy", - "git-ref":"0.6.11" - }, { "name":"libclang", - "url-x64":"https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_21.1.2-based-windows-vs2022_64.7z", - "url-ARM64":"https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_21.1.2-based-windows-vs2022_arm64.7z", - "note":"Pinned at 21.1.2; 22.x triggers shiboken parser bugs that confuse same-named enums across sibling Qt classes (QLocalSocket vs QLocalServer SocketOption, QStringConverter::Default, etc.). Revisit after PySide 6.11.1 or later." + "url-x64":"https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_22.1.8-based-windows-vs2022_64.7z", + "url-ARM64":"https://download.qt.io/development_releases/prebuilt/libclang/libclang-release_22.1.8-based-windows-vs2022_arm64.7z", + "note":"Bumped to 22.1.8 for LibPack 3.5.3. Previously held at 21.1.2 because 22.x triggered shiboken parser bugs that confused same-named enums across sibling Qt classes (QLocalSocket vs QLocalServer SocketOption, QStringConverter::Default, etc.). PySide 6.11 supports libclang 16-22 and Qt now ships the 22.1.8 prebuilt, so the revisit condition was met. Confirm the PySide/shiboken build is clean when validating this bump." }, { "name":"pyside", @@ -271,7 +267,7 @@ { "name":"libfmt", "git-repo":"https://github.com/fmtlib/fmt", - "git-ref":"12.1.0" + "git-ref":"12.2.0" }, { "name": "yamlcpp", diff --git a/create_libpack.py b/create_libpack.py index e565926..442be76 100644 --- a/create_libpack.py +++ b/create_libpack.py @@ -660,6 +660,10 @@ def prevent_sleep_mode(): exit(1) else: compiler.init_script = [init_bat] + # Ignore the per-user site-packages, which the LibPack's Python shares with any + # same-version system Python; a user-installed setuptools there shadows our pinned + # copy and breaks setup.py-based steps (PySide/Shiboken). + os.environ["PYTHONNOUSERSITE"] = "1" compiler.compile_all() # Final cleanup: delete extraneous files and remove local path references from the cMake files diff --git a/patches/coin-01-cxx20-shared-ptr-unique.patch b/patches/coin-01-cxx20-shared-ptr-unique.patch deleted file mode 100644 index 95db826..0000000 --- a/patches/coin-01-cxx20-shared-ptr-unique.patch +++ /dev/null @@ -1,10 +0,0 @@ -@@@ include/Inventor/SbByteBufferP.icc @@@ -@@ -747,16 +747,17 @@ - ze_ && ! -+( - PRIVATE( -@@ -771,22 +771,31 @@ - buffer.u --nique( -+se_count() == 1 - )) %7B%0A diff --git a/patches/quarter-01-add-QOpenGLContext-to-QuarterWidgetP.patch b/patches/quarter-01-add-QOpenGLContext-to-QuarterWidgetP.patch deleted file mode 100644 index 74c104f..0000000 --- a/patches/quarter-01-add-QOpenGLContext-to-QuarterWidgetP.patch +++ /dev/null @@ -1,5 +0,0 @@ -@@@ src\Quarter\QuarterWidgetP.cpp @@@ -@@ -2303,24 +2303,51 @@ - glue/gl.h%3E%0A%0A -+#include %3CQOpenGLContext%3E%0A%0A - #include %22Na diff --git a/path_cleaner.py b/path_cleaner.py index f81d2a7..36e490f 100644 --- a/path_cleaner.py +++ b/path_cleaner.py @@ -404,7 +404,7 @@ def install_pdb_sidecars( Most upstream CMake configs install only the DLL and the import library and leave the sidecar PDB behind in the build tree. As a result the installed - LibPack carries no debugging information for Qt, OCCT, Coin, VTK, Boost, or + LibPack carries no debugging information for Qt, OCCT, VTK, Boost, or the Python interpreter, even when built with /Zi /DEBUG. This walk fills that gap: for every DLL already installed under ``install_dir``, locate a PDB whose stem matches anywhere under ``working_dir`` (or any of the