mirror of
https://github.com/CoolProp/CoolProp.git
synced 2026-04-23 03:00:17 -04:00
* Modernize Python build system to use scikit-build-core
This commit replaces the old setuptools-based build system with a modern
scikit-build-core + CMake build system for the Python bindings.
Key changes:
- Replace setup.py with pyproject.toml using scikit-build-core backend
- Create new CMakeLists.txt for Cython module compilation
- Add FindCython.cmake helper module
- Update README from .rst to .md format
- Enable incremental builds with proper CMake dependency tracking
- Support Python 3.8-3.14 with proper Cython directives
Benefits:
- Incremental builds work correctly (only rebuild changed files)
- Modern PEP 517/518 compliant build system
- Build artifacts cached in build/{wheel_tag} directories
- Better integration with pip and modern Python tooling
- No more need for custom _py_backend build hooks
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add build-time file generation and ignore generated files
This commit adds the missing build-time steps from setup.py:
- Header generation from JSON files (generate_headers.py)
- Cython constants module generation (generate_constants_module.py)
- Copying headers, fmtlib, and BibTeX file to package directory
Also updates .gitignore to ignore:
- wrappers/Python/CoolProp/include/ (generated during build)
- wrappers/Python/CoolProp/CoolPropBibTeXLibrary.bib (copied during build)
Includes test script to verify wheel contents match between old and new build approaches.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Set CMAKE_POSITION_INDEPENDENT_CODE for shared library build
Enable -fPIC flag for all targets to ensure proper shared library compilation.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove deprecated buildbot configuration
The buildbot system is deprecated and no longer in use.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update extract_version.py to use .version file instead of setup.py
With the migration to scikit-build-core, version information is now stored
in the .version file and read by pyproject.toml. Updated the script to:
- Rename replace_setup_py() to replace_version_file()
- Update .version file instead of modifying setup.py
- Change --replace-setup-py flag to --replace-version
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update build scripts and documentation for scikit-build-core
Changes:
- Updated documentation to show modern pip-based installation
- Updated manylinux build script to use pip wheel instead of setup.py
- Updated conda metadata generator to use pip install
- Removed deprecated PyPI preparation script (replaced by `python -m build --sdist`)
All build infrastructure now uses the new scikit-build-core build system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update manylinux Docker script for scikit-build-core
Removed SETUP_PY_ARGS since cmake options are no longer passed via
setup.py arguments. The new build system uses CMake directly via
scikit-build-core. Also fixed typo and updated install_root path.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove deprecated _py_backend custom build backend
The _py_backend was a custom setuptools build backend wrapper used
with the old setup.py build system. It's no longer needed with
scikit-build-core.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Add uv package manager documentation
Added section on using uv (Astral's fast Python package manager) to
install and work with CoolProp. Includes examples for:
- Installing in current environment
- Creating new projects with CoolProp
- Running scripts with automatic environment management
- Development installations from source
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix sdist packaging and update scikit-build-core config
Changes:
- Updated pyproject.toml to use newer scikit-build-core config syntax
- cmake.minimum-version → cmake.version
- cmake.verbose → build.verbose
- Added sdist.include to ensure .version file is in source distributions
- Added .version to MANIFEST.in for completeness
This fixes the issue where building a wheel from an sdist would fail
due to missing .version file. Now sdist → wheel builds work correctly.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Rename setup.py to deprecated_setup.py
The old setuptools-based build system has been fully replaced with
scikit-build-core. Renaming setup.py to deprecated_setup.py to:
- Clearly indicate it's no longer the primary build method
- Keep it available for reference and backward compatibility
- Prevent accidental use of the old build system
Users should now use: pip install .
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update GitHub Actions workflows for new build system
Changes:
- Replace --replace-setup-py with --replace-version flag
- Update sdist build to use 'python -m build --sdist' instead of deprecated prepare_pypi.py
- Workflows now work with scikit-build-core build system
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Fix cibuildwheel to build from repository root
With scikit-build-core, the pyproject.toml is at the repository root,
not in wrappers/Python/. Updated cibuildwheel configuration:
- Changed package-dir from ./wrappers/Python/ to .
- Removed redundant CIBW_BEFORE_BUILD (dependencies are in pyproject.toml)
- Build dependencies are now automatically installed by pip from pyproject.toml
This fixes the "Multiple top-level packages discovered" error.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Set macOS deployment target to 11.0 in pyproject.toml
Configure MACOSX_DEPLOYMENT_TARGET=11.0 (Big Sur) in cibuildwheel config.
This matches the setting in GitHub Actions and ensures wheels are built
with consistent compatibility for macOS 11.0 and later.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove old pyproject.toml from wrappers/Python
This file was used by the old setuptools build system. With
scikit-build-core, the main pyproject.toml at the repository root
is now used for all Python packaging configuration.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove redundant PyPy skip selector from cibuildwheel
The 'pp*' skip selector was causing a warning because PyPy isn't enabled
in the build matrix anyway. Since we explicitly specify only CPython
versions in the build directive (cp38-*, cp39-*, etc.), the pp* skip is
unnecessary.
Fixes warning: "Invalid skip selector: 'pp*'. This selector matches a
group that wasn't enabled."
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update fmtlib from 11.1.3 to 12.0.0
Updated the fmtlib submodule to the latest stable release (12.0.0).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Remove pdsim
---------
Co-authored-by: Claude <noreply@anthropic.com>
276 lines
8.7 KiB
Python
276 lines
8.7 KiB
Python
#!/usr/bin/env python
|
|
"""
|
|
Test script to compare wheel contents between old setup.py and new scikit-build-core builds.
|
|
|
|
This script:
|
|
1. Builds a wheel using the old setup.py approach
|
|
2. Builds a wheel using the new scikit-build-core approach
|
|
3. Extracts and compares the contents
|
|
4. Reports any differences
|
|
5. Exits with code 0 if identical, 1 if different
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import tempfile
|
|
import subprocess
|
|
import zipfile
|
|
import shutil
|
|
import filecmp
|
|
import difflib
|
|
from pathlib import Path
|
|
from typing import Tuple, List, Set
|
|
|
|
# Get the repository root
|
|
REPO_ROOT = Path(__file__).parent.parent.absolute()
|
|
|
|
def run_command(cmd: List[str], cwd: Path, description: str) -> subprocess.CompletedProcess:
|
|
"""Run a command and handle errors."""
|
|
print(f" Running: {' '.join(cmd)}")
|
|
try:
|
|
result = subprocess.run(
|
|
cmd,
|
|
cwd=cwd,
|
|
capture_output=True,
|
|
text=True,
|
|
check=True
|
|
)
|
|
return result
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"ERROR: {description} failed!")
|
|
print(f" Command: {' '.join(cmd)}")
|
|
print(f" Return code: {e.returncode}")
|
|
print(f" stdout: {e.stdout}")
|
|
print(f" stderr: {e.stderr}")
|
|
sys.exit(1)
|
|
|
|
def build_old_wheel(tmpdir: Path) -> Path:
|
|
"""Build wheel using old setup.py approach."""
|
|
print("\n" + "="*80)
|
|
print("Building wheel with OLD setup.py approach...")
|
|
print("="*80)
|
|
|
|
# Change to wrappers/Python directory
|
|
build_dir = REPO_ROOT / "wrappers" / "Python"
|
|
|
|
# Clean any previous builds
|
|
dist_dir = build_dir / "dist"
|
|
if dist_dir.exists():
|
|
shutil.rmtree(dist_dir)
|
|
|
|
# Build the wheel
|
|
run_command(
|
|
[sys.executable, "setup.py", "bdist_wheel"],
|
|
cwd=build_dir,
|
|
description="Old wheel build"
|
|
)
|
|
|
|
# Find the built wheel
|
|
wheels = list((build_dir / "dist").glob("*.whl"))
|
|
if not wheels:
|
|
print("ERROR: No wheel file found in dist/")
|
|
sys.exit(1)
|
|
|
|
wheel_path = wheels[0]
|
|
print(f" Built wheel: {wheel_path.name}")
|
|
|
|
# Copy to temp directory
|
|
dest = tmpdir / wheel_path.name
|
|
shutil.copy2(wheel_path, dest)
|
|
|
|
return dest
|
|
|
|
def build_new_wheel(tmpdir: Path) -> Path:
|
|
"""Build wheel using new scikit-build-core approach."""
|
|
print("\n" + "="*80)
|
|
print("Building wheel with NEW scikit-build-core approach...")
|
|
print("="*80)
|
|
|
|
# Build from repository root
|
|
run_command(
|
|
[sys.executable, "-m", "pip", "wheel", "--no-deps", "--no-build-isolation",
|
|
"-w", str(tmpdir), str(REPO_ROOT)],
|
|
cwd=REPO_ROOT,
|
|
description="New wheel build"
|
|
)
|
|
|
|
# Find the built wheel
|
|
wheels = list(tmpdir.glob("*.whl"))
|
|
if not wheels:
|
|
print("ERROR: No wheel file found")
|
|
sys.exit(1)
|
|
|
|
wheel_path = wheels[0]
|
|
print(f" Built wheel: {wheel_path.name}")
|
|
|
|
return wheel_path
|
|
|
|
def extract_wheel(wheel_path: Path, extract_dir: Path) -> Set[str]:
|
|
"""Extract wheel and return set of relative file paths."""
|
|
print(f"\n Extracting {wheel_path.name}...")
|
|
|
|
with zipfile.ZipFile(wheel_path, 'r') as zf:
|
|
zf.extractall(extract_dir)
|
|
|
|
# Get all files recursively
|
|
files = set()
|
|
for root, dirs, filenames in os.walk(extract_dir):
|
|
for filename in filenames:
|
|
filepath = Path(root) / filename
|
|
relpath = filepath.relative_to(extract_dir)
|
|
files.add(str(relpath))
|
|
|
|
print(f" Extracted {len(files)} files")
|
|
return files
|
|
|
|
def compare_file_contents(file1: Path, file2: Path) -> Tuple[bool, str]:
|
|
"""Compare two files. Returns (identical, diff_text)."""
|
|
# Binary comparison first
|
|
if filecmp.cmp(file1, file2, shallow=False):
|
|
return True, ""
|
|
|
|
# If different, try to generate a useful diff
|
|
try:
|
|
with open(file1, 'r', encoding='utf-8', errors='ignore') as f1:
|
|
lines1 = f1.readlines()
|
|
with open(file2, 'r', encoding='utf-8', errors='ignore') as f2:
|
|
lines2 = f2.readlines()
|
|
|
|
diff = list(difflib.unified_diff(
|
|
lines1, lines2,
|
|
fromfile=str(file1),
|
|
tofile=str(file2),
|
|
lineterm='',
|
|
n=3
|
|
))
|
|
|
|
if diff:
|
|
return False, '\n'.join(diff[:50]) # Limit diff output
|
|
except:
|
|
pass
|
|
|
|
return False, "(binary files differ)"
|
|
|
|
def compare_wheels(old_dir: Path, new_dir: Path, old_files: Set[str], new_files: Set[str]) -> bool:
|
|
"""Compare extracted wheel contents. Returns True if identical."""
|
|
print("\n" + "="*80)
|
|
print("Comparing wheel contents...")
|
|
print("="*80)
|
|
|
|
# Find differences in file lists
|
|
only_in_old = old_files - new_files
|
|
only_in_new = new_files - old_files
|
|
common_files = old_files & new_files
|
|
|
|
# Filter out metadata files and irrelevant files that are expected to differ
|
|
def is_ignorable(path: str) -> bool:
|
|
parts = Path(path).parts
|
|
filename = Path(path).name
|
|
return (len(parts) > 0 and
|
|
(parts[0].endswith('.dist-info') or
|
|
parts[0].endswith('.egg-info') or
|
|
path.endswith('RECORD') or
|
|
path.endswith('WHEEL') or
|
|
filename == '.DS_Store' or
|
|
filename == '.gitignore' or
|
|
path.startswith('_py_backend/')))
|
|
|
|
only_in_old = {f for f in only_in_old if not is_ignorable(f)}
|
|
only_in_new = {f for f in only_in_new if not is_ignorable(f)}
|
|
|
|
all_identical = True
|
|
|
|
# Report files only in old
|
|
if only_in_old:
|
|
print(f"\n⚠️ Files ONLY in OLD wheel ({len(only_in_old)}):")
|
|
for f in sorted(only_in_old)[:20]: # Limit output
|
|
print(f" - {f}")
|
|
if len(only_in_old) > 20:
|
|
print(f" ... and {len(only_in_old) - 20} more")
|
|
all_identical = False
|
|
|
|
# Report files only in new
|
|
if only_in_new:
|
|
print(f"\n⚠️ Files ONLY in NEW wheel ({len(only_in_new)}):")
|
|
for f in sorted(only_in_new)[:20]: # Limit output
|
|
print(f" - {f}")
|
|
if len(only_in_new) > 20:
|
|
print(f" ... and {len(only_in_new) - 20} more")
|
|
all_identical = False
|
|
|
|
# Compare common files (excluding binary .so files which are expected to differ)
|
|
non_binary_files = [f for f in common_files
|
|
if not is_ignorable(f) and not f.endswith('.so')]
|
|
|
|
print(f"\n Comparing {len(non_binary_files)} common non-binary files...")
|
|
print(f" (Skipping {len([f for f in common_files if f.endswith('.so')])} binary .so files)")
|
|
different_files = []
|
|
|
|
for relpath in sorted(non_binary_files):
|
|
file1 = old_dir / relpath
|
|
file2 = new_dir / relpath
|
|
|
|
identical, diff_text = compare_file_contents(file1, file2)
|
|
if not identical:
|
|
different_files.append((relpath, diff_text))
|
|
|
|
if different_files:
|
|
print(f"\n⚠️ Files with DIFFERENT contents ({len(different_files)}):")
|
|
for relpath, diff_text in different_files[:10]: # Limit output
|
|
print(f"\n File: {relpath}")
|
|
if diff_text:
|
|
print(" Diff preview:")
|
|
for line in diff_text.split('\n')[:20]:
|
|
print(f" {line}")
|
|
if len(different_files) > 10:
|
|
print(f" ... and {len(different_files) - 10} more files differ")
|
|
all_identical = False
|
|
|
|
# Summary
|
|
print("\n" + "="*80)
|
|
if all_identical:
|
|
print("✅ SUCCESS: Wheel contents are IDENTICAL!")
|
|
else:
|
|
print("❌ FAILURE: Wheel contents DIFFER!")
|
|
print(f" - Files only in old: {len(only_in_old)}")
|
|
print(f" - Files only in new: {len(only_in_new)}")
|
|
print(f" - Files with different content: {len(different_files)}")
|
|
print("="*80)
|
|
|
|
return all_identical
|
|
|
|
def main():
|
|
print("="*80)
|
|
print("CoolProp Wheel Comparison Test")
|
|
print("="*80)
|
|
print(f"Repository: {REPO_ROOT}")
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir_str:
|
|
tmpdir = Path(tmpdir_str)
|
|
|
|
# Create subdirectories
|
|
old_build = tmpdir / "old_build"
|
|
new_build = tmpdir / "new_build"
|
|
old_extract = tmpdir / "old_extract"
|
|
new_extract = tmpdir / "new_extract"
|
|
|
|
for d in [old_build, new_build, old_extract, new_extract]:
|
|
d.mkdir()
|
|
|
|
# Build both wheels
|
|
old_wheel = build_old_wheel(old_build)
|
|
new_wheel = build_new_wheel(new_build)
|
|
|
|
# Extract both wheels
|
|
old_files = extract_wheel(old_wheel, old_extract)
|
|
new_files = extract_wheel(new_wheel, new_extract)
|
|
|
|
# Compare contents
|
|
identical = compare_wheels(old_extract, new_extract, old_files, new_files)
|
|
|
|
# Exit with appropriate code
|
|
sys.exit(0 if identical else 1)
|
|
|
|
if __name__ == "__main__":
|
|
main()
|