From 76f0b7f0c4793154000fdd8965273447ff88c75e Mon Sep 17 00:00:00 2001 From: John Sirois Date: Tue, 28 Feb 2023 21:07:09 -0800 Subject: [PATCH] Attempt "cross-builds" of sdists for foreign platforms. The "cross-building" is in scare-quotes because this is not actually cross-building, it is just attempting a build of the sdist, and, if successful, seeing if the resulting wheel matches the foreign platform. This enables sdist-only projects to be used in foregin platform Pex operations when it turns out the sdist is multi-platform. Closes #2073 --- pex/pip/foreign_platform.py | 3 - pex/pip/tool.py | 3 +- pex/resolve/locked_resolve.py | 20 +-- pex/resolver.py | 25 ++- tests/integration/cli/commands/test_export.py | 7 +- tests/integration/cli/commands/test_lock.py | 155 ++++++++++-------- tests/integration/test_issue_2073.py | 114 +++++++++++++ tests/resolve/test_locked_resolve.py | 7 - tests/test_pip.py | 74 ++++----- 9 files changed, 268 insertions(+), 140 deletions(-) create mode 100644 tests/integration/test_issue_2073.py diff --git a/pex/pip/foreign_platform.py b/pex/pip/foreign_platform.py index 08760bad1..9072c1b6f 100644 --- a/pex/pip/foreign_platform.py +++ b/pex/pip/foreign_platform.py @@ -121,9 +121,6 @@ def patch(target): json.dump(patched_environment, markers_fp) env = dict(_PEX_PATCHED_MARKERS_FILE=markers_fp.name) - if isinstance(target, AbbreviatedPlatform): - args = tuple(iter_platform_args(target.platform, target.manylinux)) - compatible_tags = target.supported_tags if compatible_tags: env.update(patch_tags(compatible_tags).env) diff --git a/pex/pip/tool.py b/pex/pip/tool.py index f07d9cc4e..75601cb01 100644 --- a/pex/pip/tool.py +++ b/pex/pip/tool.py @@ -425,8 +425,7 @@ def spawn_download_distributions( download_cmd = ["download", "--dest", download_dir] extra_env = {} # type: Dict[str, str] - if not isinstance(target, LocalInterpreter) or not build: - # If we're not targeting a local interpreter, we can't build wheels from sdists. + if not build: download_cmd.extend(["--only-binary", ":all:"]) if not use_wheel: diff --git a/pex/resolve/locked_resolve.py b/pex/resolve/locked_resolve.py index 9141c8501..04920d8d9 100644 --- a/pex/resolve/locked_resolve.py +++ b/pex/resolve/locked_resolve.py @@ -551,21 +551,11 @@ def resolve( # type: (...) -> Union[Resolved, Error] is_local_interpreter = isinstance(target, LocalInterpreter) - if not use_wheel: - if not build: - return Error( - "Cannot both ignore wheels (use_wheel=False) and refrain from building " - "distributions (build=False)." - ) - elif not is_local_interpreter: - return Error( - "Cannot ignore wheels (use_wheel=False) when resolving for a platform: given " - "{platform_description}".format( - platform_description=target.render_description() - ) - ) - if not is_local_interpreter: - build = False + if not use_wheel and not build: + return Error( + "Cannot both ignore wheels (use_wheel=False) and refrain from building " + "distributions (build=False)." + ) repository = defaultdict(list) # type: DefaultDict[ProjectName, List[LockedRequirement]] for locked_requirement in self.locked_requirements: diff --git a/pex/resolver.py b/pex/resolver.py index 40d1914d4..403abecf3 100644 --- a/pex/resolver.py +++ b/pex/resolver.py @@ -17,11 +17,12 @@ from pex.auth import PasswordEntry from pex.common import safe_mkdir, safe_mkdtemp from pex.compatibility import unquote, urlparse -from pex.dist_metadata import DistMetadata, Distribution, Requirement +from pex.dist_metadata import DistMetadata, Distribution, ProjectNameAndVersion, Requirement from pex.fingerprinted_distribution import FingerprintedDistribution from pex.jobs import Raise, SpawnedJob, execute_parallel from pex.network_configuration import NetworkConfiguration from pex.orderedset import OrderedSet +from pex.pep_425 import CompatibilityTags from pex.pep_503 import ProjectName from pex.pex_info import PexInfo from pex.pip.download_observer import DownloadObserver @@ -323,7 +324,27 @@ def finalize_build(self): ) ) wheel = wheels[0] - return InstallRequest.create(self.request.target, os.path.join(self.dist_dir, wheel)) + wheel_path = os.path.join(self.dist_dir, wheel) + if self.request.target.is_foreign: + wheel_tags = CompatibilityTags.from_wheel(wheel_path) + if not self.request.target.supported_tags.compatible_tags(wheel_tags): + project_name_and_version = ProjectNameAndVersion.from_filename(wheel_path) + raise ValueError( + "No pre-built wheel was available for {project_name} {version}.{eol}" + "Successfully built the wheel {wheel} from the sdist {sdist} but it is not " + "compatible with the requested foreign target {target}.{eol}" + "You'll need to build a wheel from {sdist} on the foreign target platform and " + "make it available to Pex via a `--find-links` repo or a custom " + "`--index`.".format( + project_name=project_name_and_version.project_name, + version=project_name_and_version.version, + eol=os.linesep, + wheel=wheel, + sdist=os.path.basename(self.request.source_path), + target=self.request.target.render_description(), + ) + ) + return InstallRequest.create(self.request.target, wheel_path) @attr.s(frozen=True) diff --git a/tests/integration/cli/commands/test_export.py b/tests/integration/cli/commands/test_export.py index f373e5bcd..c1730fb9b 100644 --- a/tests/integration/cli/commands/test_export.py +++ b/tests/integration/cli/commands/test_export.py @@ -269,7 +269,8 @@ def test_export_respects_target(tmpdir): assert dedent( """\ ansicolors==1.1.8 \\ - --hash=md5:abcd1234 + --hash=md5:abcd1234 \\ + --hash=sha1:ef567890 pywin32==227 \\ --hash=sha256:spameggs """ @@ -284,8 +285,8 @@ def test_export_respects_target(tmpdir): } ), ), ( - "A win32 foreign target should get all wheels but no sdists since we can't cross-build " - "sdists." + "A win32 foreign target should get both ansicolors cross-platform artifacts as well as " + "the platform-specific pywin32 wheel." ) assert ( diff --git a/tests/integration/cli/commands/test_lock.py b/tests/integration/cli/commands/test_lock.py index 51c581891..95f01a30e 100644 --- a/tests/integration/cli/commands/test_lock.py +++ b/tests/integration/cli/commands/test_lock.py @@ -19,6 +19,7 @@ from pex.pep_440 import Version from pex.pep_503 import ProjectName from pex.pip.version import PipVersion +from pex.platforms import Platform from pex.resolve.locked_resolve import Artifact, LockedRequirement from pex.resolve.lockfile import json_codec from pex.resolve.lockfile.download_manager import DownloadedArtifact @@ -27,8 +28,9 @@ from pex.resolve.resolver_configuration import ResolverVersion from pex.resolve.testing import normalize_locked_resolve from pex.sorted_tuple import SortedTuple -from pex.targets import LocalInterpreter +from pex.targets import AbbreviatedPlatform, LocalInterpreter from pex.testing import ( + IS_LINUX, IS_LINUX_ARM64, IS_MAC, IS_PYPY, @@ -249,6 +251,11 @@ def test_create_universal_python_unsupported(): def test_create_universal_platform_check(tmpdir): # type: (Any) -> None + foreign_platform_310 = ( + "macosx_10.9_x86_64-cp-310-cp310" if IS_LINUX else "linux_x86_64-cp-310-cp310" + ) + abbreviated_platform_310 = AbbreviatedPlatform.create(Platform.create(foreign_platform_310)) + complete_platform = os.path.join(str(tmpdir), "complete-platform.json") run_pex3("interpreter", "inspect", "--markers", "--tags", "-v", "-i2", "-o", complete_platform) @@ -260,7 +267,7 @@ def test_create_universal_platform_check(tmpdir): "--style", "universal", "--platform", - "linux_x86_64-cp-310-cp310", + foreign_platform_310, "ansicolors==1.1.8", ).assert_success() run_pex3( @@ -273,28 +280,76 @@ def test_create_universal_platform_check(tmpdir): "ansicolors==1.1.8", ).assert_success() - # But if we exclude wheels, we should now fail any platform check. + # If we exclude wheels for an abbreviated platform, we should still be OK for ansicolors since + # it is cross-platform, and we can "cross-build" it. The one exception is Python 2.7. Even + # though the ansicolors wheel should be universal - it does build for py2 and py3, it is not + # marked as such and thus Python 2 builds ansicolors-1.1.8-py2-none-any.whl and Python 3 builds + # ansicolors-1.1.8-py3-none-any.whl. result = run_pex3( "lock", "create", "--style", "universal", "--platform", - "linux_x86_64-cp-310-cp310", + foreign_platform_310, "--no-wheel", "ansicolors==1.1.8", ) + if sys.version_info[0] == 2: + result.assert_failure() + assert ( + re.search( + r"No pre-built wheel was available for ansicolors 1\.1\.8\.{eol}" + r"Successfully built the wheel ansicolors-1\.1\.8-py2-none-any\.whl from the sdist " + r"ansicolors-1\.1\.8\.zip but it is not compatible with the requested foreign target " + r"{foreign_target}\.{eol}" + r"You'll need to build a wheel from ansicolors-1\.1\.8\.zip on the foreign target " + r"platform and make it available to Pex via a `--find-links` repo or a custom " + r"`--index`\." + r"".format( + foreign_target=re.escape(abbreviated_platform_310.render_description()), + eol=os.linesep, + ), + result.error, + ) + is not None + ), result.error + else: + result.assert_success() + + # But not for psutil because it is platform-specific, and we cannot "cross-build" it. + result = run_pex3( + "lock", + "create", + "--style", + "universal", + "--platform", + foreign_platform_310, + "--no-wheel", + "psutil==5.9.1", + ) result.assert_failure() + assert ( - dedent( - """\ - Failed to resolve compatible artifacts from lock for 1 target: - 1. cp310-cp310-linux_x86_64: - Cannot ignore wheels (use_wheel=False) when resolving for a platform: given abbreviated platform cp310-cp310-linux_x86_64 - """ + re.search( + r"No pre-built wheel was available for psutil 5\.9\.1\.{eol}" + r"Successfully built the wheel psutil-5\.9\.1-\S+\.whl from the sdist " + r"psutil-5\.9\.1\.tar\.gz but it is not compatible with the requested foreign target " + r"{foreign_target}\.{eol}" + r"You'll need to build a wheel from psutil-5\.9\.1\.tar\.gz on the foreign target " + r"platform and make it available to Pex via a `--find-links` repo or a custom " + r"`--index`\." + r"".format( + foreign_target=re.escape(abbreviated_platform_310.render_description()), + eol=os.linesep, + ), + result.error, ) - in result.error - ) + is not None + ), result.error + + # If we exclude wheels for a complete platform, we should still be OK for ansicolors since it is + # cross-platform. run_pex3( "lock", "create", @@ -304,79 +359,47 @@ def test_create_universal_platform_check(tmpdir): complete_platform, "--no-wheel", "ansicolors==1.1.8", - ).assert_failure() + ).assert_success() + # There are 3.10 wheels for all platforms we test here. run_pex3( "lock", "create", "--style", "universal", "--platform", - "macosx_10.9_x86_64-cp-310-cp310", + foreign_platform_310, "psutil==5.9.1", ).assert_success() - # Here there is no pre-built wheel for CPython 3.11 on any platform; so we expect failure when - # checking the --platform macosx_10.9_x86_64-cp-311-cp311 will fail. + # Here there is no pre-built wheel for CPython 3.11 on any platform; so we expect failure from + # the "cross-build" attempt. + foreign_platform_311 = ( + "macosx_10.9_x86_64-cp-311-cp311" if IS_LINUX else "linux_x86_64-cp-311-cp311" + ) result = run_pex3( "lock", "create", "--style", "universal", "--platform", - "macosx_10.9_x86_64-cp-311-cp311", + foreign_platform_311, "psutil==5.9.1", ) result.assert_failure() - assert ( - dedent( - """\ - Failed to resolve compatible artifacts from lock for 1 target: - 1. cp311-cp311-macosx_10_9_x86_64: - Failed to resolve all requirements for abbreviated platform cp311-cp311-macosx_10_9_x86_64: - - Configured with: - build: False - use_wheel: True - - Dependency on psutil not satisfied, 1 incompatible candidate found: - 1.) psutil 5.9.1 (via: psutil==5.9.1) does not have any compatible artifacts: - https://files.pythonhosted.org/packages/77/06/f9fd79449440d7217d6bf2c90998d540e125cfeffe39d214a328dadc46f4/psutil-5.9.1-cp27-cp27m-manylinux2010_i686.whl - https://files.pythonhosted.org/packages/13/71/c25adbd9b33a2e27edbe1fc84b3111a5ad97611885d7abcbdd8d1f2bb7ca/psutil-5.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl - https://files.pythonhosted.org/packages/14/06/39d7e963a6a8bbf26519de208593cdb0ddfe22918b8989f4b2363d4ab49f/psutil-5.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl - https://files.pythonhosted.org/packages/1b/53/8f0772df0a6d593bc2fcdf12f4f790bab5c4f6a77bb61a8ddaad2cbba7f8/psutil-5.9.1-cp27-cp27m-win_amd64.whl - https://files.pythonhosted.org/packages/26/b4/a58cf15ea649faa92c54f00c627aef1d50b9f1abf207485f10c967a50c95/psutil-5.9.1-cp310-cp310-win32.whl - https://files.pythonhosted.org/packages/2a/32/136cd5bf55728ea64a22b1d817890e35fc17314c46a24ee3268b65f9076f/psutil-5.9.1-cp37-cp37m-win32.whl - https://files.pythonhosted.org/packages/2c/9d/dc329b7da284677ea843f3ff4b35b8ab3b96b65a58a544b3c3f86d9d032f/psutil-5.9.1-cp27-cp27mu-manylinux2010_x86_64.whl - https://files.pythonhosted.org/packages/2d/56/54b4ed8102ce5a2f5367b4e766c1873c18f9c32cde321435d0e0ee2abcc5/psutil-5.9.1-cp27-cp27mu-manylinux2010_i686.whl - https://files.pythonhosted.org/packages/41/ec/5fd3e9388d0ed1edfdeae71799df374f4a117932646a63413fa95a121e9f/psutil-5.9.1-cp38-cp38-win32.whl - https://files.pythonhosted.org/packages/46/80/1de3a9bac336b5c8e4f7b0ff2e80c85ba237f18f2703be68884ee6798432/psutil-5.9.1-cp38-cp38-macosx_10_9_x86_64.whl - https://files.pythonhosted.org/packages/62/1f/f14225bda76417ab9bd808ff21d5cd59d5435a9796ca09b34d4cb0edcd88/psutil-5.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl - https://files.pythonhosted.org/packages/65/1d/6a112f146faee6292a6c3ee2a7f24a8e572697adb7e1c5de3d8508f647cc/psutil-5.9.1-cp36-cp36m-macosx_10_9_x86_64.whl - https://files.pythonhosted.org/packages/6b/76/a8cb69ed3566877dcbccf408f5f9d6055227ad4fed694e88809fa8506b0b/psutil-5.9.1-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl - https://files.pythonhosted.org/packages/6d/c6/6a4e46802e8690d50ba6a56c7f79ac283e703fcfa0fdae8e41909c8cef1f/psutil-5.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl - https://files.pythonhosted.org/packages/73/1a/d78f2f2de2aad6628415d2a48917cabc2c7fb0c3a31c7cdf187cffa4eb36/psutil-5.9.1-cp36-cp36m-win_amd64.whl - https://files.pythonhosted.org/packages/7e/52/a02dc53e26714a339c8b4972d8e3f268e4db8905f5d1a3a100f1e40b6fa7/psutil-5.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl - https://files.pythonhosted.org/packages/7e/8d/e0a66123fa98e309597815de518b47a7a6c571a8f886fc8d4db2331fd2ab/psutil-5.9.1-cp27-cp27m-win32.whl - https://files.pythonhosted.org/packages/85/4d/78173e3dffb74c5fa87914908f143473d0b8b9183f9d275333679a4e4649/psutil-5.9.1-cp36-cp36m-win32.whl - https://files.pythonhosted.org/packages/97/f6/0180e58dd1359da7d6fbc27d04dac6fb500dc758b6f4b65407608bb13170/psutil-5.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl - https://files.pythonhosted.org/packages/9d/41/d5f2db2ab7f5dff2fa795993a0cd6fa8a8f39ca197c3a86857875333ec10/psutil-5.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl - https://files.pythonhosted.org/packages/9f/ca/84ce3e48b3ca2f0f74314d89929b3a523220f3f4a8dff395d6ef74dadef3/psutil-5.9.1-cp39-cp39-macosx_10_9_x86_64.whl - https://files.pythonhosted.org/packages/a9/97/b7e3532d97d527349701d2143c3f868733b94e2db6f531b07811b698f549/psutil-5.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl - https://files.pythonhosted.org/packages/b1/d2/c5374a784567c1e42ee8a589b1b42e2bd6e14c7be3c234d84360ab3a0a39/psutil-5.9.1-cp39-cp39-win32.whl - https://files.pythonhosted.org/packages/b2/ad/65e2b2b97677f98d718388dc11b2a9d7f177ebbae5eef72547a32bc28911/psutil-5.9.1-cp38-cp38-win_amd64.whl - https://files.pythonhosted.org/packages/c0/5a/2ac88d5265b711c8aa4e786825b38d5d0b1e5ecbdd0ce78e9b04a820d247/psutil-5.9.1-cp310-cp310-win_amd64.whl - https://files.pythonhosted.org/packages/cf/29/ad704a45960bfb52ef8bf0beb9c41c09ce92d61c40333f03e9a03f246c22/psutil-5.9.1-cp27-cp27m-manylinux2010_x86_64.whl - https://files.pythonhosted.org/packages/d1/16/6239e76ab5d990dc7866bc22a80585f73421588d63b42884d607f5f815e2/psutil-5.9.1-cp310-cp310-macosx_10_9_x86_64.whl - https://files.pythonhosted.org/packages/d6/de/0999ea2562b96d7165812606b18f7169307b60cd378bc29cf3673322c7e9/psutil-5.9.1.tar.gz - https://files.pythonhosted.org/packages/d6/ef/fd4dc9085e3879c3af63fe60667dd3b71adf50d030b5549315f4a619271b/psutil-5.9.1-cp37-cp37m-macosx_10_9_x86_64.whl - https://files.pythonhosted.org/packages/df/88/427f3959855fcb3ab04891e00c026a246892feb11b20433db814b7a24405/psutil-5.9.1-cp37-cp37m-win_amd64.whl - https://files.pythonhosted.org/packages/e0/ac/fd6f098969d49f046083ac032e6788d9f861903596fb9555a02bf50a1238/psutil-5.9.1-cp39-cp39-win_amd64.whl - https://files.pythonhosted.org/packages/fd/ba/c5a3f46f351ab609cc0be6a563e492900c57e3d5c9bda0b79b84d8c3eae9/psutil-5.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl - """ - ) - in result.error - ) + abbreviated_platform_311 = AbbreviatedPlatform.create(Platform.create(foreign_platform_311)) + assert re.search( + r"No pre-built wheel was available for psutil 5\.9\.1\.{eol}" + r"Successfully built the wheel psutil-5\.9\.1-\S+\.whl from the sdist " + r"psutil-5\.9\.1\.tar\.gz but it is not compatible with the requested foreign target " + r"{foreign_target}\.{eol}" + r"You'll need to build a wheel from psutil-5\.9\.1\.tar\.gz on the foreign target " + r"platform and make it available to Pex via a `--find-links` repo or a custom " + r"`--index`\.".format( + foreign_target=abbreviated_platform_311.render_description(), eol=os.linesep + ), + result.error, + ), result.error UPDATE_LOCKFILE_CONTENTS = """\ diff --git a/tests/integration/test_issue_2073.py b/tests/integration/test_issue_2073.py new file mode 100644 index 000000000..d9e427305 --- /dev/null +++ b/tests/integration/test_issue_2073.py @@ -0,0 +1,114 @@ +# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). +import os.path +import re +import subprocess + +from pex.cli.testing import run_pex3 +from pex.platforms import Platform +from pex.targets import AbbreviatedPlatform +from pex.testing import IS_LINUX, IntegResults, run_pex_command +from pex.typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing import Any + + +FOREIGN_PLATFORM_311 = ( + "macosx_10.9_x86_64-cp-311-cp311" if IS_LINUX else "linux_x86_64-cp-311-cp311" +) +ABBREVIATED_FOREIGN_PLATFORM_311 = AbbreviatedPlatform.create(Platform.create(FOREIGN_PLATFORM_311)) + + +def assert_psutil_cross_build_failure(result): + # type: (IntegResults) -> None + result.assert_failure() + assert ( + re.search( + r"No pre-built wheel was available for psutil 5\.9\.1\.{eol}" + r"Successfully built the wheel psutil-5\.9\.1-\S+\.whl from the sdist " + r"psutil-5\.9\.1\.tar\.gz but it is not compatible with the requested foreign target " + r"{foreign_target}\.{eol}" + r"You'll need to build a wheel from psutil-5\.9\.1\.tar\.gz on the foreign target platform " + r"and make it available to Pex via a `--find-links` repo or a custom `--index`\.".format( + eol=os.linesep, + foreign_target=ABBREVIATED_FOREIGN_PLATFORM_311.render_description(), + ), + result.error, + ) + is not None + ), result.error + + +def assert_cowsay_cross_build_success( + tmpdir, # type: Any + *args # type: str +): + pex = os.path.join(str(tmpdir), "pex") + run_pex_command( + args=["cowsay==5.0", "-c", "cowsay", "--platform", FOREIGN_PLATFORM_311, "-o", pex] + + list(args) + ).assert_success() + assert "5.0" == subprocess.check_output(args=[pex, "--version"]).decode("utf-8").strip() + + +def test_standard_resolve_foreign_platform_yolo_cross_build(tmpdir): + # type: (Any) -> None + + # There is no pre-built wheel for CPython 3.11 on any platform; so we expect failure from the + # "cross-build" attempt. + assert_psutil_cross_build_failure( + run_pex_command(args=["psutil==5.9.1", "--platform", FOREIGN_PLATFORM_311]) + ) + + # The cowsay 5.0 distribution is sdist-only. We should grabe this and attempt a build to see if + # we succeed and if the resulting wheel is compatible, which it should be since cowsay 5.0 is + # known to build to a py2.py3 universal wheel. + assert_cowsay_cross_build_success(tmpdir) + + +def create_lock( + lock, # type: str + *args # type: str +): + # type: (...) -> IntegResults + return run_pex3( + "lock", + "create", + "--style", + "universal", + "--target-system", + "linux", + "--target-system", + "mac", + "-o", + lock, + "--indent", + "2", + *args + ) + + +def test_lock_create_foreign_platform_yolo_cross_build(tmpdir): + # type: (Any) -> None + + lock = os.path.join(str(tmpdir), "lock") + + assert_psutil_cross_build_failure( + create_lock(lock, "--platform", FOREIGN_PLATFORM_311, "psutil==5.9.1") + ) + + create_lock(lock, "--platform", FOREIGN_PLATFORM_311, "cowsay==5.0").assert_success() + + +def test_lock_resolve_foreign_platform_yolo_cross_build(tmpdir): + # type: (Any) -> None + + lock = os.path.join(str(tmpdir), "lock") + create_lock(lock, "psutil==5.9.1", "cowsay==5.0").assert_success() + + assert_psutil_cross_build_failure( + run_pex_command(args=["psutil==5.9.1", "--platform", FOREIGN_PLATFORM_311, "--lock", lock]) + ) + + assert_cowsay_cross_build_success(tmpdir, "--lock", lock) diff --git a/tests/resolve/test_locked_resolve.py b/tests/resolve/test_locked_resolve.py index b23aa529d..79c7c0e4f 100644 --- a/tests/resolve/test_locked_resolve.py +++ b/tests/resolve/test_locked_resolve.py @@ -279,13 +279,6 @@ def test_invalid_configuration( "(build=False).", ) - platform_target = platform("linux-x86_64-cp-37-m") - assert_error( - ansicolors_exotic.resolve(platform_target, [req("ansicolors")], use_wheel=False), - "Cannot ignore wheels (use_wheel=False) when resolving for a platform: given " - "{target_description}".format(target_description=platform_target.render_description()), - ) - def test_platform_resolve(ansicolors_exotic): # type: (LockedResolve) -> None diff --git a/tests/test_pip.py b/tests/test_pip.py index 1541fc829..59d9ccadb 100644 --- a/tests/test_pip.py +++ b/tests/test_pip.py @@ -21,7 +21,7 @@ from pex.platforms import Platform from pex.resolve.configured_resolver import ConfiguredResolver from pex.targets import AbbreviatedPlatform, LocalInterpreter, Target -from pex.testing import PY310, ensure_python_interpreter, environment_as +from pex.testing import IS_LINUX, PY310, ensure_python_interpreter, environment_as from pex.typing import TYPE_CHECKING from pex.variables import ENV @@ -106,18 +106,28 @@ def test_no_duplicate_constraints_pex_warnings( ) +@pytest.mark.skipif( + not IS_LINUX + or not any( + ( + "manylinux2014_x86_64" == platform.platform + for platform in PythonInterpreter.get().supported_platforms + ) + ), + reason="Test requires a manylinux2014_x86_64 compatible interpreter.", +) @applicable_pip_versions def test_download_platform_issues_1355( create_pip, # type: CreatePip version, # type: PipVersionValue - current_interpreter, # type: PythonInterpreter + py38, # type: PythonInterpreter tmpdir, # type: Any ): # type: (...) -> None - pip = create_pip(current_interpreter, version=version) + pip = create_pip(py38, version=version) download_dir = os.path.join(str(tmpdir), "downloads") - def download_ansicolors( + def download_pyarrow( target=None, # type: Optional[Target] package_index_configuration=None, # type: Optional[PackageIndexConfiguration] ): @@ -125,49 +135,29 @@ def download_ansicolors( safe_rmtree(download_dir) return pip.spawn_download_distributions( download_dir=download_dir, - requirements=["ansicolors==1.0.2"], + requirements=["pyarrow==4.0.1"], transitive=False, target=target, package_index_configuration=package_index_configuration, ) - def assert_ansicolors_downloaded(target=None): - # type: (Optional[Target]) -> None - download_ansicolors(target=target).wait() - assert ["ansicolors-1.0.2.tar.gz"] == os.listdir(download_dir) - - # The only ansicolors 1.0.2 dist on PyPI is an sdist and we should be able to download one of - # those with the current interpreter since we have an interpreter in hand to build a wheel from - # it with later. - assert_ansicolors_downloaded() - assert_ansicolors_downloaded(target=targets.current()) - assert_ansicolors_downloaded(target=LocalInterpreter.create(current_interpreter)) - - wheel_dir = os.path.join(str(tmpdir), "wheels") - pip.spawn_build_wheels( - distributions=glob.glob(os.path.join(download_dir, "*.tar.gz")), - wheel_dir=wheel_dir, - interpreter=current_interpreter, - ).wait() - built_wheels = glob.glob(os.path.join(wheel_dir, "*.whl")) - assert len(built_wheels) == 1 - - ansicolors_wheel = built_wheels[0] - local_wheel_repo = PackageIndexConfiguration.create(find_links=[wheel_dir]) - current_platform = AbbreviatedPlatform.create(current_interpreter.platform) - - # We should fail to find a wheel for ansicolors 1.0.2 and thus fail to download for a target - # Platform, even if that target platform happens to match the current interpreter we're - # executing Pip with. - with pytest.raises(Job.Error): - download_ansicolors(target=current_platform).wait() - - # If we point the target Platform to a find-links repo with the wheel just-built though, the - # download should proceed without error. - download_ansicolors( - target=current_platform, package_index_configuration=local_wheel_repo - ).wait() - assert [os.path.basename(ansicolors_wheel)] == os.listdir(download_dir) + def assert_pyarrow_downloaded( + expected_wheel, # type: str + target=None, # type: Optional[Target] + ): + # type: (...) -> None + download_pyarrow(target=target).wait() + assert [expected_wheel] == os.listdir(download_dir) + + assert_pyarrow_downloaded( + "pyarrow-4.0.1-cp38-cp38-manylinux2014_x86_64.whl", target=LocalInterpreter.create(py38) + ) + assert_pyarrow_downloaded( + "pyarrow-4.0.1-cp38-cp38-manylinux2010_x86_64.whl", + target=AbbreviatedPlatform.create( + Platform.create("linux-x86_64-cp-38-cp38"), manylinux="manylinux2010" + ), + ) def assert_download_platform_markers_issue_1366(