diff --git a/flake.nix b/flake.nix index 9bcdf9a..64b5038 100644 --- a/flake.nix +++ b/flake.nix @@ -15,9 +15,10 @@ let forAllSystems = f: nixpkgs.lib.genAttrs [ "x86_64-linux" "aarch64-linux" ] (sys: let pkgs = import nixpkgs {system = sys; overlays = [self.overlays.default];}; in f pkgs); + overlayBaseInstance = import ./overlay.nix nixpkgs.outPath; myOverlay = pkgs: - import ./overlay.nix { + overlayBaseInstance { inherit pkgs; oldPkgs = nixpkgs-old.legacyPackages.${pkgs.hostPlatform.system}; }; diff --git a/importlib.nix b/importlib.nix index 81a8104..7ad172d 100644 --- a/importlib.nix +++ b/importlib.nix @@ -1,11 +1,15 @@ -{stdenv, fetchPypi, python26Packages }: python26Packages.buildPythonPackage rec { +{stdenv, fetchPypi, buildPythonPackage, bootstrapped-pip, setuptools }: buildPythonPackage rec { pname = "importlib"; version = "1.0.2"; - pyproject = true; + format = "other"; src = fetchPypi { inherit pname version; extension = "tar.gz"; hash = "sha256-JNCWaqdoWbKISFgD8xAHZRL4c3fmya/pwoxSqMzdMow="; }; + + nativeBuildInputs = [bootstrapped-pip setuptools]; + + pythonImportsCheck = []; } diff --git a/jinja2.nix b/jinja2.nix index 24b7e91..49cfe15 100644 --- a/jinja2.nix +++ b/jinja2.nix @@ -1,4 +1,4 @@ -{stdenv, fetchPypi, buildPythonPackage, setuptools }: buildPythonPackage rec { +{stdenv, fetchPypi, buildPythonPackage, setuptools, argparse }: buildPythonPackage rec { pname = "Jinja2"; version = "2.5.2"; format = "setuptools"; @@ -10,7 +10,7 @@ hash = "sha256-GQQPAbOp2MY+TVeTb3hxCQgZm2k3CrRKD3QH3YkfAZs="; }; - propagatedBuildInputs = [setuptools]; + propagatedBuildInputs = [setuptools argparse]; #dependencies = [importlib]; } diff --git a/lxml.nix b/lxml.nix new file mode 100644 index 0000000..49cfe15 --- /dev/null +++ b/lxml.nix @@ -0,0 +1,16 @@ +{stdenv, fetchPypi, buildPythonPackage, setuptools, argparse }: buildPythonPackage rec { + pname = "Jinja2"; + version = "2.5.2"; + format = "setuptools"; + #pyproject = true; + + src = fetchPypi { + inherit pname version; + extension = "tar.gz"; + hash = "sha256-GQQPAbOp2MY+TVeTb3hxCQgZm2k3CrRKD3QH3YkfAZs="; + }; + + propagatedBuildInputs = [setuptools argparse]; + + #dependencies = [importlib]; +} diff --git a/overlay.nix b/overlay.nix index 9e95788..75aa04f 100644 --- a/overlay.nix +++ b/overlay.nix @@ -1,4 +1,4 @@ -{ pkgs, oldPkgs }: +nixpkgsPath: { pkgs, oldPkgs }: let bommels = pkgs.lib.makeScope pkgs.newScope (self: rec { # python26 = pkgs.callPackage ./python26/default.nix { @@ -6,14 +6,14 @@ bommels = pkgs.lib.makeScope pkgs.newScope (self: rec { # xlibsWrapper = pkgs.xlibsWrapper; # }; - setuptools = self.callPackage ./setuptools.nix { inherit pythonPackages; }; + #setuptools = self.callPackage ./setuptools.nix { inherit pythonPackages; }; #wrapPython = pkgs.callPackage ./python26/wrap-python.nix { python = python26; }; #mkPythonDerivation = pkgs.callPackage ./python26/mk-python-derivation.nix { inherit setuptools wrapPython; python = python26; }; #bootstrapped-pip = pkgs.callPackage ./python26/bootstrapped-pip.nix { python = python26; }; #buildPythonPackage = pkgs.callPackage ./python26/build-python-package.nix { inherit bootstrapped-pip mkPythonDerivation; python = python26; }; #pip = pkgs.callPackage ./pip.nix { inherit python26 buildPythonPackage; }; - pythonInterpreters = self.callPackage ./python-interpreters.nix {}; + pythonInterpreters = self.callPackage ./python-interpreters.nix { inherit nixpkgsPath;}; inherit (pythonInterpreters) python26; python26Packages = python26.pkgs; pythonPackages = python26Packages; diff --git a/python-imports-check-hook.sh b/python-imports-check-hook.sh index c3c758d..11b84c0 100644 --- a/python-imports-check-hook.sh +++ b/python-imports-check-hook.sh @@ -18,7 +18,7 @@ pythonImportsCheckPhase() { # Python modules and namespaces names are Python identifiers, which must not contain spaces. # See https://docs.python.org/3/reference/lexical_analysis.html # shellcheck disable=SC2048,SC2086 - (cd "$pythonImportsCheckOutput" && @pythonCheckInterpreter@ -c 'import sys; import importlib; list(map(lambda mod: importlib.import_module(mod), sys.argv[1:]))' ${pythonImportsCheck[*]}) + (cd "$pythonImportsCheckOutput" && @pythonCheckInterpreter@ -c 'import sys; import imp; importlib = imp.load_source("importlib", "@importlib@"); list(map(lambda mod: importlib.import_module(mod), sys.argv[1:]))' ${pythonImportsCheck[*]}) fi } diff --git a/python-interpreters.nix b/python-interpreters.nix index 7c7acd5..c7efa22 100644 --- a/python-interpreters.nix +++ b/python-interpreters.nix @@ -1,4 +1,5 @@ { + nixpkgsPath, __splicedPackages, callPackage, fetchzip, @@ -8,15 +9,14 @@ makeScopeWithSplicing', pythonPackagesExtensions, stdenv, - path }@args: ( let - pythonBase = path + "/pkgs/development/interpreters/python"; + pythonBase = nixpkgsPath + "/pkgs/development/interpreters/python"; # Common passthru for all Python interpreters. - passthruFun = import ( pythonBase + "/passthrufun.nix") args; + passthruFun = import ./python26/passthrufun.nix (args // { inherit pythonBase; }); fixupString = s: lib.replaceString "2.7" "2.6" (lib.replaceString "2.7.1" "2.6.9" s); pythonRaw = ((callPackage "${pythonBase}/cpython/2.7" { diff --git a/python-overrides.nix b/python-overrides.nix index 5d33e5b..7021ae6 100644 --- a/python-overrides.nix +++ b/python-overrides.nix @@ -7,10 +7,17 @@ with self; with super; let pythonInterpreter = python.pythonOnBuildForHost.interpreter; + pythonCheckInterpreter = python.interpreter; pythonSitePackages = python.sitePackages; in rec { bootstrapped-pip = toPythonModule (callPackage ./python26/bootstrapped-pip.nix {}); + pip = callPackage ./python26/pip.nix {}; + argparse = callPackage ./python26/argparse.nix {}; + + hookOverrides = { + inherit pipInstallHook pythonImportsCheckHook; + }; pipInstallHookMine = callPackage ( { makePythonHook, pip }: @@ -28,17 +35,19 @@ rec { makePythonHook { name = "python-imports-check-hook"; substitutions = { - inherit pythonInterpreter pythonSitePackages; + inherit pythonInterpreter pythonCheckInterpreter pythonSitePackages; + importlib = ./python26/importlib.py; }; } ./python-imports-check-hook.sh ) { }; pythonImportsCheckHook = pythonImportsCheckHookMine; + importlib = callPackage ./importlib.nix {}; setuptools = toPythonModule (callPackage ./setuptools.nix { }); wheel = callPackage ./python26/wheel.nix { }; #pip = pip.overridePythonAttrs (callPackage pipFunction {}); bommels = { - jinja2 = callPackage ./jinja2.nix { }; + jinja2 = callPackage ./jinja2.nix { }; }; } diff --git a/python26/argparse.nix b/python26/argparse.nix new file mode 100644 index 0000000..1e28711 --- /dev/null +++ b/python26/argparse.nix @@ -0,0 +1,46 @@ +{ lib +, buildPythonPackage +, fetchPypi +, jsonschema +, bootstrapped-pip +, setuptools +, fetchFromGitHub +, breakpointHook +}: + +buildPythonPackage rec { + pname = "argparse"; + version = "1.4.0"; + format = "other"; + + #src = fetchPypi { + # inherit pname version; + # sha256 = "1ebb8ad7e26b448e9caa4773d2357849bf80ff9e313964bcaf79cbf0201a1648"; + #}; + src= fetchFromGitHub { + inherit pname version; + owner="ThomasWaldmann"; + repo ="argparse"; + tag = "r140"; + hash = "sha256-L0cJ44aO3Wd2P/8vykJlOEZu7W2AsNCcAaGDeHtn6b0="; + }; + + #buildInputs = [ pytest pytestcov coverage ]; + nativeBuildInputs = [ bootstrapped-pip setuptools breakpointHook]; + + catchConflicts = false; + + doCheck = false; + + #pythonImportsCheck = [ "wheel" ]; + + # We add this flag to ignore the copy installed by bootstrapped-pip + pipInstallFlags = [ "--ignore-installed" ]; + #pipInstallFlags = ["--upgrade wheel"]; + + meta = { + description = "A built-package format for Python"; + license = with lib.licenses; [ mit ]; + homepage = "https://bitbucket.org/pypa/wheel/"; + }; +} diff --git a/python26/bootstrapped-pip.nix b/python26/bootstrapped-pip.nix index 4b1c836..5940d81 100644 --- a/python26/bootstrapped-pip.nix +++ b/python26/bootstrapped-pip.nix @@ -1,56 +1,118 @@ -{ lib, stdenv, python, fetchurl, makeWrapper, unzip }: +{ + lib, + stdenv, + fetchurl, + fetchFromGitHub, + python, + makeWrapper, + unzip, + pipInstallHookMine, + setuptoolsBuildHook, + wheel, + pip, + setuptools, + breakpointHook +}: let - wheel_source = fetchurl { - url = "https://pypi.python.org/packages/py2.py3/w/wheel/wheel-0.29.0-py2.py3-none-any.whl"; - sha256 = "ea8033fc9905804e652f75474d33410a07404c1a78dd3c949a66863bd1050ebd"; - }; - setuptools_source = fetchurl { - url = "https://files.pythonhosted.org/packages/b8/cb/b919f52dd81b4b2210d0c5529b6b629a4002e08d49a90183605d1181b10c/setuptools-30.2.0-py2.py3-none-any.whl"; - sha256 = "b7e7b28d6a728ea38953d66e12ef400c3c153c523539f1b3997c5a42f3770ff1"; - }; - argparse_source = fetchurl { - url = "https://pypi.python.org/packages/2.7/a/argparse/argparse-1.4.0-py2.py3-none-any.whl"; - sha256 = "0533cr5w14da8wdb2q4py6aizvbvsdbk3sj7m1jx9lwznvnlf5n3"; - }; -in stdenv.mkDerivation rec { - name = "${python.libPrefix}-bootstrapped-pip-${version}"; - version = "9.0.1"; - - src = fetchurl { +pipBootstrap = (fetchurl { url = "https://files.pythonhosted.org/packages/b6/ac/7015eb97dc749283ffdec1c3a88ddb8ae03b8fad0f0e611408f196358da3/pip-9.0.1-py2.py3-none-any.whl"; sha256 = "690b762c0a8460c303c089d5d0be034fb15a5ea2b75bdf565f40421f542fefb0"; + }); + argparse = fetchFromGitHub { + pname = "argparse"; + version = "1.4.0"; + owner="ThomasWaldmann"; + repo ="argparse"; + tag = "r140"; + hash = "sha256-L0cJ44aO3Wd2P/8vykJlOEZu7W2AsNCcAaGDeHtn6b0="; }; +in +stdenv.mkDerivation rec { + pname = "pip"; + inherit (pip) version; + name = "${python.libPrefix}-bootstrapped-${pname}-${version}"; - unpackPhase = '' - mkdir -p $out/${python.sitePackages} - unzip -d $out/${python.sitePackages} $src - unzip -d $out/${python.sitePackages} ${setuptools_source} - unzip -d $out/${python.sitePackages} ${wheel_source} - '' + lib.optionalString (python.isPy26 or true) '' - unzip -d $out/${python.sitePackages} ${argparse_source} - ''; + srcs = [ + wheel.src + pip.src + setuptools.src + argparse + ]; + sourceRoot = "."; - patchPhase = '' + dontUseSetuptoolsBuild = true; + dontUsePipInstall = true; + + # Should be propagatedNativeBuildInputs + propagatedBuildInputs = [ + # Override to remove dependencies to prevent infinite recursion. + (pipInstallHookMine.override { pip = null; }) + (setuptoolsBuildHook.override { + setuptools = null; + wheel = null; + }) + ]; + + postPatch = '' mkdir -p $out/bin ''; - buildInputs = [ python makeWrapper unzip ]; + nativeBuildInputs = [ + makeWrapper + unzip + breakpointHook + ]; + buildInputs = [ python ]; - installPhase = '' - runHook preInstall + dontBuild = true; - # install pip binary - echo '#!${python.interpreter}' > $out/bin/pip - echo 'import sys;from pip import main' >> $out/bin/pip - echo 'sys.exit(main())' >> $out/bin/pip - chmod +x $out/bin/pip + installPhase = + lib.optionalString (!stdenv.hostPlatform.isWindows) '' + export SETUPTOOLS_INSTALL_WINDOWS_SPECIFIC_FILES=0 + '' + + '' + echo '#!${python.interpreter}' > hostPip + echo 'import sys;from pip import main' >> hostPip + echo 'sys.exit(main())' >> hostPip + chmod +x hostPip - # wrap binaries with PYTHONPATH - for f in $out/bin/*; do - wrapProgram $f --prefix PYTHONPATH ":" $out/${python.sitePackages}/ - done - runHook postInstall - ''; + + # Give folders a known name + mv pip* pip + mv setuptools* setuptools + mv wheel* wheel + mv argparse* argparse + # Set up PYTHONPATH: + # - pip and setuptools need to be in PYTHONPATH to install setuptools, wheel, and pip. + # - $out is where we are installing to and takes precedence, and is where wheel will end so we can install pip. + export PYTHONPATH="$out/${python.sitePackages}:$(pwd)/pip/pip:$(pwd)/setuptools:$(pwd)/setuptools/pkg_resources:$PYTHONPATH" + + echo "Building setuptools wheel..." + pushd setuptools + ${python.pythonOnBuildForHost.interpreter} ${pipBootstrap}/pip install --no-index --prefix=$out --ignore-installed --no-dependencies --no-cache . + popd + + echo "Building wheel wheel..." + pushd wheel + ${python.pythonOnBuildForHost.interpreter} ${pipBootstrap}/pip install --no-index --prefix=$out --ignore-installed --no-dependencies --no-cache . + popd + + echo "Building pip wheel..." + pushd pip + ${python.pythonOnBuildForHost.interpreter} ${pipBootstrap}/pip install --no-index --prefix=$out --ignore-installed --no-dependencies --no-cache . + popd + + echo "Building argparse wheel..." + pushd argparse + ${python.pythonOnBuildForHost.interpreter} ${pipBootstrap}/pip install --prefix=$out --no-cache . + popd + ''; + + meta = { + description = "Version of pip used for bootstrapping"; + license = lib.unique (pip.meta.license ++ setuptools.meta.license ++ wheel.meta.license); + homepage = pip.meta.homepage; + }; } diff --git a/python26/importlib.py b/python26/importlib.py new file mode 100644 index 0000000..ad31a1a --- /dev/null +++ b/python26/importlib.py @@ -0,0 +1,38 @@ +"""Backport of importlib.import_module from 3.x.""" +# While not critical (and in no way guaranteed!), it would be nice to keep this +# code compatible with Python 2.3. +import sys + +def _resolve_name(name, package, level): + """Return the absolute name of the module to be imported.""" + if not hasattr(package, 'rindex'): + raise ValueError("'package' not set to a string") + dot = len(package) + for x in xrange(level, 1, -1): + try: + dot = package.rindex('.', 0, dot) + except ValueError: + raise ValueError("attempted relative import beyond top-level " + "package") + return "%s.%s" % (package[:dot], name) + + +def import_module(name, package=None): + """Import a module. + + The 'package' argument is required when performing a relative import. It + specifies the package to use as the anchor point from which to resolve the + relative import to an absolute import. + + """ + if name.startswith('.'): + if not package: + raise TypeError("relative imports require the 'package' argument") + level = 0 + for character in name: + if character != '.': + break + level += 1 + name = _resolve_name(name[level:], package, level) + __import__(name) + return sys.modules[name] diff --git a/python26/passthrufun.nix b/python26/passthrufun.nix new file mode 100644 index 0000000..517de42 --- /dev/null +++ b/python26/passthrufun.nix @@ -0,0 +1,152 @@ +{ + lib, + stdenv, + callPackage, + pythonPackagesExtensions, + config, + makeScopeWithSplicing', + pythonBase, + ... +}: + +{ + implementation, + libPrefix, + executable, + sourceVersion, + pythonVersion, + packageOverrides, + sitePackages, + hasDistutilsCxxPatch, + pythonOnBuildForBuild, + pythonOnBuildForHost, + pythonOnBuildForTarget, + pythonOnHostForHost, + pythonOnTargetForTarget, + pythonAttr ? null, + self, # is pythonOnHostForTarget +}: +let + pythonPackages = + let + ensurePythonModules = + items: + let + exceptions = [ + stdenv + ]; + providesSetupHook = lib.attrByPath [ "provides" "setupHook" ] false; + valid = + value: pythonPackages.hasPythonModule value || providesSetupHook value || lib.elem value exceptions; + func = + name: value: + if lib.isDerivation value then + lib.extendDerivation ( + valid value + || throw "${name} should use `buildPythonPackage` or `toPythonModule` if it is to be part of the Python packages set." + ) { } value + else + value; + in + lib.mapAttrs func items; + in + ensurePythonModules ( + callPackage + # Function that when called + # - imports python-packages.nix + # - adds spliced package sets to the package set + # - applies overrides from `packageOverrides` and `pythonPackagesOverlays`. + ( + { + pkgs, + stdenv, + python, + overrides, + }: + let + pythonPackagesFun = import "${pythonBase}/python-packages-base.nix" { + inherit stdenv pkgs lib; + python = self; + }; + otherSplices = { + selfBuildBuild = pythonOnBuildForBuild.pkgs; + selfBuildHost = pythonOnBuildForHost.pkgs; + selfBuildTarget = pythonOnBuildForTarget.pkgs; + selfHostHost = pythonOnHostForHost.pkgs; + selfTargetTarget = pythonOnTargetForTarget.pkgs or { }; # There is no Python TargetTarget. + }; + hooks = self: dontUse: (import "${pythonBase}/hooks/default.nix" self dontUse) // (overrides self dontUse).hookOverrides; + keep = self: hooks self { }; + optionalExtensions = cond: as: lib.optionals cond as; + pythonExtension = import "${pythonBase}/../../../top-level/python-packages.nix"; + python2Extension = import "${pythonBase}/../../../top-level/python2-packages.nix"; + extensions = lib.composeManyExtensions ( + [ + hooks + pythonExtension + ] + ++ (optionalExtensions (!self.isPy3k) [ + python2Extension + ]) + ++ pythonPackagesExtensions + ++ [ + overrides + ] + ); + aliases = + self: super: + lib.optionalAttrs config.allowAliases (import "${pythonBase}/../../../top-level/python-aliases.nix" lib self super); + in + makeScopeWithSplicing' { + inherit otherSplices keep; + f = lib.extends (lib.composeExtensions aliases extensions) pythonPackagesFun; + } + ) + { + overrides = packageOverrides; + python = self; + } + ); +in +rec { + isPy26 = pythonVersion == "2.6"; + isPy27 = pythonVersion == "2.7"; + isPy37 = pythonVersion == "3.7"; + isPy38 = pythonVersion == "3.8"; + isPy39 = pythonVersion == "3.9"; + isPy310 = pythonVersion == "3.10"; + isPy311 = pythonVersion == "3.11"; + isPy312 = pythonVersion == "3.12"; + isPy313 = pythonVersion == "3.13"; + isPy314 = pythonVersion == "3.14"; + isPy2 = lib.strings.substring 0 1 pythonVersion == "2"; + isPy3 = lib.strings.substring 0 1 pythonVersion == "3"; + isPy3k = isPy3; + isPyPy = lib.hasInfix "pypy" interpreter; + + buildEnv = callPackage "${pythonBase}/wrapper.nix" { + python = self; + inherit (pythonPackages) requiredPythonModules; + }; + withPackages = import "${pythonBase}/with-packages.nix" { inherit buildEnv pythonPackages; }; + pkgs = pythonPackages; + interpreter = "${self}/bin/${executable}"; + inherit + executable + implementation + libPrefix + pythonVersion + sitePackages + ; + inherit sourceVersion; + pythonAtLeast = lib.versionAtLeast pythonVersion; + pythonOlder = lib.versionOlder pythonVersion; + inherit hasDistutilsCxxPatch; + inherit pythonOnBuildForHost; + + tests = callPackage "${pythonBase}/tests.nix" { + python = self; + }; + + inherit pythonAttr; +} diff --git a/python26/pip.nix b/python26/pip.nix new file mode 100644 index 0000000..06d781b --- /dev/null +++ b/python26/pip.nix @@ -0,0 +1,48 @@ +{ + lib, + buildPythonPackage, + bootstrapped-pip, + fetchFromGitHub, + mock, + scripttest, + virtualenv, + pretend, + pytest, +}: + +buildPythonPackage rec { + pname = "pip"; + version = "9.0.1"; + format = "other"; + + src = fetchFromGitHub { + owner = "pypa"; + repo = pname; + rev = version; + hash = "sha256-U9Qy8s6T00khlu1ZVxYZeoMHLOFKp/Fsg+Hu8Yi3OAU="; + name = "${pname}-${version}-source"; + }; + + nativeBuildInputs = [ bootstrapped-pip ]; + + # pip detects that we already have bootstrapped_pip "installed", so we need + # to force it a little. + pipInstallFlags = [ "--ignore-installed" ]; + + nativeCheckInputs = [ + mock + scripttest + virtualenv + pretend + pytest + ]; + # Pip wants pytest, but tests are not distributed + doCheck = false; + + meta = { + description = "PyPA recommended tool for installing Python packages"; + license = with lib.licenses; [ mit ]; + homepage = "https://pip.pypa.io/"; + priority = 10; + }; +} diff --git a/python26/wheel.nix b/python26/wheel.nix index 5b5340b..cc7c55d 100644 --- a/python26/wheel.nix +++ b/python26/wheel.nix @@ -2,9 +2,12 @@ , buildPythonPackage , fetchPypi , jsonschema +, argparse , bootstrapped-pip , setuptools , fetchFromGitHub +, breakpointHook +, python }: buildPythonPackage rec { @@ -25,16 +28,22 @@ buildPythonPackage rec { }; #buildInputs = [ pytest pytestcov coverage ]; - nativeBuildInputs = [ bootstrapped-pip setuptools ]; + propagatedBuildInputs = [ argparse ]; + nativeBuildInputs = [ bootstrapped-pip setuptools breakpointHook]; - #catchConflicts = false; + catchConflicts = false; doCheck = false; - #pythonImportsCheck = [ "wheel" ]; + pythonImportsCheck = [ "wheel" ]; + + preInstall = '' + #export PYTHONPATH=${argparse}/${python.sitePackages} + ''; # We add this flag to ignore the copy installed by bootstrapped-pip - pipInstallFlags = [ "--ignore-installed" ]; + pipInstallFlags = [ "--ignore-installed --no-deps" ]; + #pipInstallFlags = ["--upgrade wheel"]; meta = { description = "A built-package format for Python"; diff --git a/result b/result index c3f6e6e..fe2e6c6 120000 --- a/result +++ b/result @@ -1 +1 @@ -/nix/store/qr2d62ybl4vm30hdn5bzxm0387hq95ik-python2.6-wheel-0.29.0 \ No newline at end of file +/nix/store/sdpslgfqlmmbjaz93m7w9c5c01fs8wxn-python2.6-Jinja2-2.5.2 \ No newline at end of file