From 33ebcf688aec7ab5524c4584e267c1c8bb48df20 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 13 Jun 2026 19:02:48 -0500 Subject: [PATCH 1/2] Update libraries for a 3.5.1 release --- compile_all.py | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ config.json | 50 ++++++++++++++++++++++---------------------- 2 files changed, 81 insertions(+), 25 deletions(-) diff --git a/compile_all.py b/compile_all.py index 5465e64..f737d07 100644 --- a/compile_all.py +++ b/compile_all.py @@ -385,6 +385,18 @@ def get_cmake_options(self) -> List[str]: def compile_all(self): # This option borks Tcl by making it find the wrong paths: remove it os.environ.pop("NoDefaultCurrentDirectoryInExePath", None) + # Keep pip's wheel and HTTP cache inside the build tree rather than the user-global + # location (%LOCALAPPDATA%\pip\cache). This isolates the build both ways: wheels built + # here never leak into the developer's machine-wide cache, and a stale wheel from + # unrelated local pip activity can never be pulled into the LibPack. The directory lives + # under working-/ and so is not part of the shipped LibPack. Setting it in the + # environment covers every pip subprocess uniformly (requirements, tooling, pip self- + # upgrade, individual installs) regardless of how each call constructs its env. + pip_cache_dir = os.path.abspath( + os.path.join(os.path.dirname(self.install_dir), "pip-cache") + ) + os.makedirs(pip_cache_dir, exist_ok=True) + os.environ["PIP_CACHE_DIR"] = pip_cache_dir for item in self.config["content"]: # All build methods are named using "build_XXX" where XXX is the name of the package in the config file os.chdir(item["name"]) @@ -832,6 +844,9 @@ def _install_debug_library_aliases(self): ("lib/libxml2d.lib", "lib/libxml2.lib"), ("lib/libxsltd.lib", "lib/libxslt.lib"), ("lib/libexsltd.lib", "lib/libexslt.lib"), + # lxml's setup.py hardcodes 'iconv' in its Windows link line; win-iconv builds with + # the debug 'd' postfix, so expose iconvd.lib under the undecorated name it expects. + ("lib/iconvd.lib", "lib/iconv.lib"), ) for src, dst in aliases: src_path = os.path.join(self.install_dir, src) @@ -917,6 +932,24 @@ def _install_python_requirements(self, requirements): config_settings=config_settings + (("setup-args", "-Duse-pythran=false"),), ) + def _native_pkgconf_path(self, env) -> Optional[str]: + """Return the path to pkgconf-pypi's bundled native pkgconf executable, or None if the + pkgconf package is not yet installed in the LibPack. Uses the package's documented + get_executable() API rather than hardcoding its internal .bin layout.""" + try: + result = subprocess.run( + [self.python_exe(), "-c", "import pkgconf; print(pkgconf.get_executable())"], + capture_output=True, + text=True, + env=env, + ) + except OSError: + return None + if result.returncode != 0: + return None + path = result.stdout.strip() + return path if path and os.path.exists(path) else None + def _run_pip_install( self, requirements, no_build_isolation, no_binary_packages, config_settings=() ): @@ -971,6 +1004,18 @@ def _run_pip_install( # without this, pkg-config silently returns no flags and lxml's compile # cannot find . env["FORCE_PKGCONF_PYPI"] = "1" + # lxml's setup.py (and meson) honor the PKG_CONFIG environment variable to locate + # the pkg-config executable. Point it straight at pkgconf-pypi's bundled native + # binary rather than its pkg-config.exe console-script wrapper: under Python 3.14 the + # wrapper's subinterpreter entrypoint intermittently returns no flags at all, which + # makes lxml fall back to a bare "/usr/include/libxml2" and fail to find + # . The native binary has no such failure mode. pkgconf is + # installed as part of _DEBUG_BUILD_REQUIRED_TOOLING before the requirements that + # consume it, so this resolves on the main install; on the tooling bootstrap pass it + # is not yet present and PKG_CONFIG is simply left unset. + native_pkgconf = self._native_pkgconf_path(env) + if native_pkgconf: + env["PKG_CONFIG"] = native_pkgconf scripts_dir = os.path.join(self.install_dir, "bin", "Scripts") bin_dir = os.path.join(self.install_dir, "bin") env["PATH"] = scripts_dir + os.pathsep + bin_dir + os.pathsep + env.get("PATH", "") @@ -1063,6 +1108,9 @@ def build_qt(self, options: dict): f" Attempting to use default path {build_dir}. \n\nIf the build fails, consider making a temp directory to work in.\n" ) + if os.path.exists(build_dir): + print(f" Removing existing Qt build directory {build_dir}") + shutil.rmtree(build_dir, onerror=remove_readonly) os.makedirs(build_dir, exist_ok=True) old_cwd = os.getcwd() os.chdir(build_dir) @@ -1819,6 +1867,14 @@ def build_hdf5(self, _: None): "-D HDF5_ENABLE_Z_LIB_SUPPORT=ON", "-D ZLIB_USE_EXTERNAL=ON", ] + if sys.platform.startswith("win32"): + # HDF5 compiles the same sources into both a static and a shared library. Under the + # default Visual Studio generator, MSBuild's project-level parallelism builds those two + # targets concurrently, so the shared object PDB is written by competing cl.exe + # processes and the build dies with "C1090: PDB API call failed, error code '3'". Ninja + # schedules the whole graph through a single job pool and sidesteps that race, the same + # workaround build_vtk uses for its PDB race. + extra_args.extend(["-G", "Ninja"]) self._build_standard_cmake(extra_args) def build_medfile(self, _: None): diff --git a/config.json b/config.json index 314454c..195f62d 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { - "FreeCAD-version":"1.2.0", - "LibPack-version":"3.5.0", + "FreeCAD-version":"26.3.0", + "LibPack-version":"3.5.1beta1", "content": [ { "name":"libiconv", @@ -11,8 +11,8 @@ { "name":"libxml2", "git-repo":"https://github.com/GNOME/libxml2", - "git-ref":"v2.13.9", - "note": "Debug-only package. Source-built so that lxml has a libxml2 to link against under Py_DEBUG. Release uses lxml's PyPI wheel which bundles libxml2 internally." + "git-ref":"v2.15.2", + "note": "Debug-only package. Source-built so that lxml has a libxml2 to link against under Py_DEBUG. Pinned to the version lxml 6.1.1 bundles in its release wheel, so the Debug source build matches the Release ABI. Release uses lxml's PyPI wheel which bundles libxml2 internally." }, { "name":"libxslt", @@ -53,33 +53,33 @@ "annotated-types==0.7.0", "anyio==4.13.0", "attrs==23.2.0", - "certifi==2026.4.22", + "certifi==2026.5.20", "charset-normalizer==3.4.7", - "click==8.3.3", + "click==8.4.1", "cmake==4.3.2", "cog==0.16.12", "colorama==0.4.6", "configparser==7.2.0", "contourpy==1.3.3", "cycler==0.12.1", - "debugpy==1.8.20", + "debugpy==1.8.21", "definitions==0.2.0", "defusedxml==0.7.1", - "elementpath==5.1.1", + "elementpath==5.1.2", "fastapi==0.118.3", "fonttools==4.63.0", "h11==0.16.0", - "httptools==0.7.1", - "idna==3.15", + "httptools==0.8.0", + "idna==3.18", "ifcopenshell==0.8.5; platform_machine != \"ARM64\"", "isodate==0.7.2", "joblib==1.5.3", "kiwisolver==1.5.0", "lark==1.3.1", - "lxml==6.1.0", - "matplotlib==3.10.9", + "lxml==6.1.1", + "matplotlib==3.11.0", "nltk==3.9.4", - "numpy==2.4.4", + "numpy==2.4.6", "packaging==26.2", "pillow==12.2.0", "ply==3.11", @@ -87,13 +87,13 @@ "pydantic==2.13.4", "pydantic_core==2.46.4", "pyparsing==3.3.2", - "pyshp==3.0.3", + "pyshp==3.0.12", "pysolar==0.13", "python-dateutil==2.9.0.post0", "python-dotenv==1.2.2", "PyYAML==6.0.3", "regex==2026.5.9", - "requests==2.34.1", + "requests==2.34.2", "rpdb2==2.0.0.1.2", "scipy==1.17.1", "sets==0.3.2", @@ -103,13 +103,13 @@ "sniffio==1.3.1", "starlette==0.48.0", "structlog==24.4.0", - "tqdm==4.67.3", + "tqdm==4.68.2", "typing-inspection==0.4.2", "typing_extensions==4.15.0", "urllib3==2.7.0", - "uvicorn==0.46.0", + "uvicorn==0.49.0", "vermin==1.8.0", - "watchfiles==1.1.1", + "watchfiles==1.2.0", "websockets==16.0", "wheel==0.47.0", "xmlschema==4.3.1" @@ -183,12 +183,12 @@ { "name":"vtk", "git-repo":"https://gitlab.kitware.com/vtk/vtk.git", - "git-ref":"v9.6.1" + "git-ref":"v9.6.2" }, { "name":"harfbuzz", "git-repo":"https://github.com/harfbuzz/harfbuzz", - "git-ref":"14.2.0" + "git-ref":"14.2.1" }, { "name":"freetype", @@ -198,12 +198,12 @@ { "name":"tcl", "git-repo":"https://github.com/tcltk/tcl", - "git-ref":"core-8-6-17" + "git-ref":"core-8-6-18" }, { "name":"tk", "git-repo":"https://github.com/tcltk/tk", - "git-ref":"core-8-6-17" + "git-ref":"core-8-6-18" }, { "name": "rapidjson", @@ -245,11 +245,11 @@ { "name":"gmsh", "git-repo":"https://gitlab.onelab.info/gmsh/gmsh", - "git-hash":"23c47b0e6a91f2f67afcbe3bf9ad1b5c253e96e2", + "git-hash":"8fb091efce58de4ecaea56dcb90545e67cdc06b2", "patches": [ "patches/gmsh-01-windows_stdint.patch" ], - "note": "git hash from 2026-05-08, with support for OCCT 8.0.0" + "note": "git hash from 2026-06-10 (latest master), with support for OCCT 8.0.0" }, { "name":"pycxx", @@ -296,7 +296,7 @@ { "name": "googletest", "git-repo": "https://github.com/google/googletest", - "git-hash": "d72f9c8aea6817cdf1ca0ac10887f328de7f3da2" + "git-hash": "7140cd416cecd7462a8aae488024abeee55598e4" }, { "name": "ifcopenshell", From d041f3994f897b705e9c9f9bdf1532286a81faa7 Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sun, 14 Jun 2026 19:08:27 -0500 Subject: [PATCH 2/2] Bump version to 3.5.1 --- config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.json b/config.json index 195f62d..a57ba1d 100644 --- a/config.json +++ b/config.json @@ -1,6 +1,6 @@ { "FreeCAD-version":"26.3.0", - "LibPack-version":"3.5.1beta1", + "LibPack-version":"3.5.1", "content": [ { "name":"libiconv",