diff --git a/pynixify/base.py b/pynixify/base.py index 39eee63..1c8d5c1 100644 --- a/pynixify/base.py +++ b/pynixify/base.py @@ -25,6 +25,7 @@ class PackageMetadata: description: Optional[str] license: Optional[str] url: Optional[str] + _fmt: Optional[str] = "pyproject" @dataclass class Package: diff --git a/pynixify/data/flitcore_patch.diff b/pynixify/data/flitcore_patch.diff new file mode 100644 index 0000000..bb74c57 --- /dev/null +++ b/pynixify/data/flitcore_patch.diff @@ -0,0 +1,40 @@ +diff --git a/flit_core/flit_core/buildapi.py b/flit_core/flit_core/buildapi.py +index 963bf61..5190b7e 100644 +--- a/flit_core/flit_core/buildapi.py ++++ b/flit_core/flit_core/buildapi.py +@@ -3,6 +3,7 @@ import logging + import io + import os + import os.path as osp ++import sys + from pathlib import Path + + from .common import ( +@@ -13,6 +14,19 @@ from .config import read_flit_config + from .wheel import make_wheel_in, _write_wheel_file + from .sdist import SdistBuilder + ++def _write_pynixify_files(config_settings, deps): ++ if config_settings is not None and "PYNIXIFY_OUT" in config_settings: ++ from pathlib import Path ++ import json ++ pynix_out = Path(config_settings['PYNIXIFY_OUT']) ++ for target in ("tests", "setup", "install"): ++ fp = (pynix_out / ("%s_requires.txt" % target)).open("w") ++ fp.write('\n'.join([str(req) for req in deps])) ++ fp.write('\nflit_core') ++ fp.close() ++ with (pynix_out / 'meta.json').open('w') as fp: ++ json.dump({"version": None, "url": None, "license": None, "description": None}, fp) ++ + log = logging.getLogger(__name__) + + # PEP 517 specifies that the CWD will always be the source tree +@@ -70,6 +84,7 @@ prepare_metadata_for_build_editable = prepare_metadata_for_build_wheel + def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): + """Builds a wheel, places it in wheel_directory""" + info = make_wheel_in(pyproj_toml, Path(wheel_directory)) ++ _write_pynixify_files(config_settings, []) + return info.file.name + + def build_editable(wheel_directory, config_settings=None, metadata_directory=None): diff --git a/pynixify/data/hatchling_patch.diff b/pynixify/data/hatchling_patch.diff new file mode 100644 index 0000000..bb1f3a4 --- /dev/null +++ b/pynixify/data/hatchling_patch.diff @@ -0,0 +1,88 @@ +diff --git a/src/hatchling/build.py b/src/hatchling/build.py +index d79c1e2e..c85a837e 100644 +--- a/src/hatchling/build.py ++++ b/src/hatchling/build.py +@@ -1,6 +1,20 @@ + import os + + ++def _write_pynixify_files(config_settings, deps): ++ if config_settings is not None and "PYNIXIFY_OUT" in config_settings: ++ from pathlib import Path ++ import json ++ pynix_out = Path(config_settings['PYNIXIFY_OUT']) ++ for target in ("tests", "setup", "install"): ++ fp = (pynix_out / ("%s_requires.txt" % target)).open("w") ++ fp.write('\n'.join([str(req) for req in deps])) ++ fp.write('\nhatchling\nhatch-vcs') ++ fp.close() ++ with (pynix_out / 'meta.json').open('w') as fp: ++ json.dump({"version": None, "url": None, "license": None, "description": None}, fp) ++ ++ + def get_requires_for_build_sdist(config_settings=None): + """ + https://peps.python.org/pep-0517/#get-requires-for-build-sdist +@@ -8,6 +22,7 @@ def get_requires_for_build_sdist(config_settings=None): + from hatchling.builders.sdist import SdistBuilder + + builder = SdistBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return builder.config.dependencies + + +@@ -18,6 +33,7 @@ def build_sdist(sdist_directory, config_settings=None): + from hatchling.builders.sdist import SdistBuilder + + builder = SdistBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return os.path.basename(next(builder.build(sdist_directory, ['standard']))) + + +@@ -28,6 +44,7 @@ def get_requires_for_build_wheel(config_settings=None): + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return builder.config.dependencies + + +@@ -38,6 +55,7 @@ def build_wheel(wheel_directory, config_settings=None, metadata_directory=None): + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return os.path.basename(next(builder.build(wheel_directory, ['standard']))) + + +@@ -48,6 +66,7 @@ def get_requires_for_build_editable(config_settings=None): + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return builder.config.dependencies + + +@@ -58,6 +77,7 @@ def build_editable(wheel_directory, config_settings=None, metadata_directory=Non + from hatchling.builders.wheel import WheelBuilder + + builder = WheelBuilder(os.getcwd()) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + return os.path.basename(next(builder.build(wheel_directory, ['editable']))) + + +@@ -89,6 +109,7 @@ if 'PIP_BUILD_TRACKER' not in os.environ: + + with open(os.path.join(directory, 'METADATA'), 'w', encoding='utf-8') as f: + f.write(builder.config.core_metadata_constructor(builder.metadata)) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + + return os.path.basename(directory) + +@@ -110,5 +131,6 @@ if 'PIP_BUILD_TRACKER' not in os.environ: + + with open(os.path.join(directory, 'METADATA'), 'w', encoding='utf-8') as f: + f.write(builder.config.core_metadata_constructor(builder.metadata, extra_dependencies=extra_dependencies)) ++ _write_pynixify_files(config_settings, builder.config.dependencies) + + return os.path.basename(directory) diff --git a/pynixify/data/old_setuptools_patch.diff b/pynixify/data/old_setuptools_patch.diff index a3e4532..4b0a17f 100644 --- a/pynixify/data/old_setuptools_patch.diff +++ b/pynixify/data/old_setuptools_patch.diff @@ -2,8 +2,8 @@ diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 83882511..259effd5 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py -@@ -155,14 +155,63 @@ def _install_setup_requires(attrs): - +@@ -155,14 +155,64 @@ def _install_setup_requires(attrs): + # Honor setup.cfg's options. dist.parse_config_files(ignore_option_errors=True) - if dist.setup_requires: @@ -56,6 +56,7 @@ index 83882511..259effd5 100644 + meta_attrs = {'description', 'url', 'license', 'version'} + for meta_attr in meta_attrs: + meta[meta_attr] = attrs.get(meta_attr) ++ meta['_fmt'] = 'setuptools' + with (out / 'meta.json').open('w') as fp: + json.dump(meta, fp) + else: @@ -69,6 +70,6 @@ index 83882511..259effd5 100644 - return distutils.core.setup(**attrs) + if 'PYNIXIFY' not in os.environ: + return distutils.core.setup(**attrs) - - + + setup.__doc__ = distutils.core.setup.__doc__ diff --git a/pynixify/data/parse_setuppy_data.nix b/pynixify/data/parse_setuppy_data.nix index 7ffbe22..eb17a4c 100644 --- a/pynixify/data/parse_setuppy_data.nix +++ b/pynixify/data/parse_setuppy_data.nix @@ -1,5 +1,6 @@ { file, stdenv ? (import { }).stdenv, lib ? (import { }).lib , unzip ? (import { }).unzip, python ? (import { }).python3 +, git ? (import { }).git, fetchFromGitLab ? (import { }).fetchFromGitLab }: let @@ -21,8 +22,88 @@ let ]; }); + setuptoolsscm = python.pkgs.buildPythonPackage rec { + pname = "setuptools-scm"; + version = "7.0.5"; - pythonWithPackages = python.withPackages (ps: [ patchedSetuptools ]); + src = python.pkgs.fetchPypi { + pname = "setuptools_scm"; + inherit version; + sha256 = "sha256-Ax4Tr3cdb4krlBrbbqBFRbv5Hrxc5ox4qvP/9uH7SEQ="; + }; + + propagatedBuildInputs = [ + python.pkgs.packaging + python.pkgs.typing-extensions + python.pkgs.tomli + patchedSetuptools + ]; + + pythonImportsCheck = [ "setuptools_scm" ]; + + # check in passthru.tests.pytest to escape infinite recursion on pytest + doCheck = false; + }; + hatchling = python.pkgs.hatchling.overrideAttrs + (ps: { patches = [ ./hatchling_patch.diff ]; }); + hatchvcs = python.pkgs.buildPythonPackage rec { + pname = "hatch-vcs"; + version = "0.2.0"; + format = "pyproject"; + + disabled = python.pkgs.pythonOlder "3.7"; + + src = python.pkgs.fetchPypi { + pname = "hatch_vcs"; + inherit version; + sha256 = "sha256-mRPXM7NO7JuwNF0GJsoyFlpK0t4V0c5kPDbQnKkIq/8="; + }; + + nativeBuildInputs = [ hatchling ]; + + propagatedBuildInputs = [ hatchling setuptoolsscm ]; + + checkInputs = [ git python.pkgs.pytestCheckHook ]; + + disabledTests = [ + # incompatible with setuptools-scm>=7 + # https://github.com/ofek/hatch-vcs/issues/8 + "test_write" + ]; + + pythonImportsCheck = [ "hatch_vcs" ]; + }; + patchedflitcore = python.pkgs.flit-core.overrideAttrs + (ps: { patches = [ ./flitcore_patch.diff ]; }); + flitscm = python.pkgs.buildPythonPackage rec { + pname = "flit-scm"; + version = "1.7.0"; + + format = "pyproject"; + + src = fetchFromGitLab { + owner = "WillDaSilva"; + repo = "flit_scm"; + rev = version; + sha256 = "sha256-K5sH+oHgX/ftvhkY+vIg6wUokAP96YxrTWds3tnEtyg="; + leaveDotGit = true; + }; + + nativeBuildInputs = + [ patchedflitcore setuptoolsscm python.pkgs.tomli git ]; + propagatedBuildInputs = [ patchedflitcore setuptoolsscm ] + ++ lib.optionals (python.pkgs.pythonOlder "3.11") + [ python.pkgs.tomli ]; + }; + + pythonWithPackages = python.withPackages (ps: [ + patchedSetuptools + setuptoolsscm + hatchling + hatchvcs + flitscm + python.pkgs.pip + ]); cleanSource = src: lib.cleanSourceWith { @@ -43,10 +124,14 @@ in stdenv.mkDerivation { ''; buildPhase = '' mkdir -p $out - if ! PYNIXIFY=1 python setup.py install; then - # Indicate that fetching the result failed, but let the build succeed - touch $out/failed + if PYNIXIFY=1 python setup.py install; then + exit 0 + fi + if pip --no-cache-dir wheel --config-settings PYNIXIFY_OUT=$out --no-build-isolation $PWD; then + exit 0 fi + # Indicate that fetching the result failed, but let the build succeed + touch $out/failed ''; dontInstall = true; } diff --git a/pynixify/data/setuptools_patch.diff b/pynixify/data/setuptools_patch.diff index 6a2d123..3c05aea 100644 --- a/pynixify/data/setuptools_patch.diff +++ b/pynixify/data/setuptools_patch.diff @@ -2,8 +2,8 @@ diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 89f6f06e..2d772eed 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py -@@ -76,36 +76,64 @@ def _install_setup_requires(attrs): - +@@ -76,36 +76,65 @@ def _install_setup_requires(attrs): + # Honor setup.cfg's options. dist.parse_config_files(ignore_option_errors=True) - if dist.setup_requires: @@ -77,6 +77,7 @@ index 89f6f06e..2d772eed 100644 + meta_attrs = {'description', 'url', 'license', 'version'} + for meta_attr in meta_attrs: + meta[meta_attr] = attrs.get(meta_attr) ++ meta['_fmt'] = 'setuptools' + with (out / 'meta.json').open('w') as fp: + json.dump(meta, fp) + else: @@ -91,6 +92,6 @@ index 89f6f06e..2d772eed 100644 - return distutils.core.setup(**attrs) + if 'PYNIXIFY' not in os.environ: + return distutils.core.setup(**attrs) - - + + setup.__doc__ = distutils.core.setup.__doc__ diff --git a/pynixify/expression_builder.py b/pynixify/expression_builder.py index 11c20a6..2874b2c 100644 --- a/pynixify/expression_builder.py +++ b/pynixify/expression_builder.py @@ -38,6 +38,7 @@ buildPythonPackage rec { pname = ${package.pypi_name | nix}; version = ${version | nix}; + format = ${metadata._fmt | nix}; % if package.local_source: src = lib.cleanSource ../../..;