diff --git a/3rdparty/python/requirements.txt b/3rdparty/python/requirements.txt index bbfc1d38c4a..7f4a41c4540 100644 --- a/3rdparty/python/requirements.txt +++ b/3rdparty/python/requirements.txt @@ -33,6 +33,8 @@ node-semver==0.9.0 # These dependencies are for scripts that rules run in an external process (and for script tests). +aiohttp==3.12.15 # see: pants.backends.nfpm.native_libs.deb +aiohttp-retry==2.9.1 # see: pants.backends.nfpm.native_libs.deb elfdeps==0.2.0 # see: pants.backends.nfpm.native_libs.elfdeps # These dependencies are only for debugging Pants itself (in VSCode/PyCharm respectively), # and should never be imported. diff --git a/3rdparty/python/user_reqs.lock b/3rdparty/python/user_reqs.lock index c3d2c5d54d2..29fb4d45529 100644 --- a/3rdparty/python/user_reqs.lock +++ b/3rdparty/python/user_reqs.lock @@ -9,6 +9,165 @@ "locked_resolves": [ { "locked_requirements": [ + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", + "url": "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", + "url": "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz" + } + ], + "project_name": "aiohappyeyeballs", + "requires_dists": [], + "requires_python": ">=3.9", + "version": "2.6.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685", + "url": "https://files.pythonhosted.org/packages/85/e0/444747a9455c5de188c0f4a0173ee701e2e325d4b2550e9af84abb20cdba/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8", + "url": "https://files.pythonhosted.org/packages/12/8a/8b75f203ea7e5c21c0920d84dd24a5c0e971fe1e9b9ebbf29ae7e8e39790/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728", + "url": "https://files.pythonhosted.org/packages/17/e5/fb779a05ba6ff44d7bc1e9d24c644e876bfff5abe5454f7b854cace1b9cc/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd", + "url": "https://files.pythonhosted.org/packages/1b/96/784c785674117b4cb3877522a177ba1b5e4db9ce0fd519430b5de76eec90/aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117", + "url": "https://files.pythonhosted.org/packages/20/19/9e86722ec8e835959bd97ce8c1efa78cf361fa4531fca372551abcc9cdd6/aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0", + "url": "https://files.pythonhosted.org/packages/28/e5/55a33b991f6433569babb56018b2fb8fb9146424f8b3a0c8ecca80556762/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16", + "url": "https://files.pythonhosted.org/packages/37/4e/a22e799c2035f5d6a4ad2cf8e7c1d1bd0923192871dd6e367dafb158b14c/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50", + "url": "https://files.pythonhosted.org/packages/47/0b/a1451543475bb6b86a5cfc27861e52b14085ae232896a2654ff1231c0992/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676", + "url": "https://files.pythonhosted.org/packages/55/fd/793a23a197cc2f0d29188805cfc93aa613407f07e5f9da5cd1366afd9d7c/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7", + "url": "https://files.pythonhosted.org/packages/57/4f/ed60a591839a9d85d40694aba5cef86dde9ee51ce6cca0bb30d6eb1581e7/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9", + "url": "https://files.pythonhosted.org/packages/62/6c/94846f576f1d11df0c2e41d3001000527c0fdf63fce7e69b3927a731325d/aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe", + "url": "https://files.pythonhosted.org/packages/71/f9/0a31fcb1a7d4629ac9d8f01f1cb9242e2f9943f47f5d03215af91c3c1a26/aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2", + "url": "https://files.pythonhosted.org/packages/9b/e7/d92a237d8802ca88483906c388f7c201bbe96cd80a165ffd0ac2f6a8d59f/aiohttp-3.12.15.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b", + "url": "https://files.pythonhosted.org/packages/c6/82/1ddf0ea4f2f3afe79dffed5e8a246737cff6cbe781887a6a170299e33204/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7", + "url": "https://files.pythonhosted.org/packages/ca/bf/23a335a6670b5f5dfc6d268328e55a22651b440fca341a64fccf1eada0c6/aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5", + "url": "https://files.pythonhosted.org/packages/f8/6c/f766d0aaafcee0447fad0328da780d344489c042e25cd58fde566bf40aed/aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + } + ], + "project_name": "aiohttp", + "requires_dists": [ + "Brotli; platform_python_implementation == \"CPython\" and extra == \"speedups\"", + "aiodns>=3.3.0; extra == \"speedups\"", + "aiohappyeyeballs>=2.5.0", + "aiosignal>=1.4.0", + "async-timeout<6.0,>=4.0; python_version < \"3.11\"", + "attrs>=17.3.0", + "brotlicffi; platform_python_implementation != \"CPython\" and extra == \"speedups\"", + "frozenlist>=1.1.1", + "multidict<7.0,>=4.5", + "propcache>=0.2.0", + "yarl<2.0,>=1.17.0" + ], + "requires_python": ">=3.9", + "version": "3.12.15" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54", + "url": "https://files.pythonhosted.org/packages/1a/99/84ba7273339d0f3dfa57901b846489d2e5c2cd731470167757f1935fffbd/aiohttp_retry-2.9.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1", + "url": "https://files.pythonhosted.org/packages/9d/61/ebda4d8e3d8cfa1fd3db0fb428db2dd7461d5742cea35178277ad180b033/aiohttp_retry-2.9.1.tar.gz" + } + ], + "project_name": "aiohttp-retry", + "requires_dists": [ + "aiohttp" + ], + "requires_python": ">=3.7", + "version": "2.9.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", + "url": "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", + "url": "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz" + } + ], + "project_name": "aiosignal", + "requires_dists": [ + "frozenlist>=1.1.0", + "typing-extensions>=4.2; python_version < \"3.13\"" + ], + "requires_python": ">=3.9", + "version": "1.4.0" + }, { "artifacts": [ { @@ -89,6 +248,24 @@ "requires_python": ">=3.9", "version": "4.12.0" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", + "url": "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", + "url": "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz" + } + ], + "project_name": "attrs", + "requires_dists": [], + "requires_python": ">=3.9", + "version": "25.4.0" + }, { "artifacts": [ { @@ -608,6 +785,89 @@ "requires_python": ">=3.8", "version": "1.5.5" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", + "url": "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", + "url": "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", + "url": "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", + "url": "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", + "url": "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", + "url": "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", + "url": "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", + "url": "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", + "url": "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", + "url": "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", + "url": "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", + "url": "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", + "url": "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", + "url": "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", + "url": "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl" + } + ], + "project_name": "frozenlist", + "requires_dists": [], + "requires_python": ">=3.9", + "version": "1.8.0" + }, { "artifacts": [ { @@ -895,6 +1155,101 @@ "requires_python": ">=3.9", "version": "1.8.5" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "394fc5c42a333c9ffc3e421a4c85e08580d990e08b99f6bf35b4132114c5dcb3", + "url": "https://files.pythonhosted.org/packages/b7/da/7d22601b625e241d4f23ef1ebff8acfc60da633c9e7e7922e24d10f592b3/multidict-6.7.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "14c9e076eede3b54c636f8ce1c9c252b5f057c62131211f0ceeec273810c9721", + "url": "https://files.pythonhosted.org/packages/17/e4/67b5c27bd17c085a5ea8f1ec05b8a3e5cba0ca734bfcad5560fb129e70ca/multidict-6.7.0-cp311-cp311-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "0a13fb8e748dfc94749f622de065dd5c1def7e0d2216dba72b1d8069a389c6ff", + "url": "https://files.pythonhosted.org/packages/23/ef/43d1c3ba205b5dec93dc97f3fba179dfa47910fc73aaaea4f7ceb41cec2a/multidict-6.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "a265acbb7bb33a3a2d626afbe756371dce0279e7b17f4f4eda406459c2b5ff1c", + "url": "https://files.pythonhosted.org/packages/31/61/0c2d50241ada71ff61a79518db85ada85fdabfcf395d5968dae1cbda04e5/multidict-6.7.0-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "4d409aa42a94c0b3fa617708ef5276dfe81012ba6753a0370fcc9d0195d0a1fc", + "url": "https://files.pythonhosted.org/packages/34/9e/5c727587644d67b2ed479041e4b1c58e30afc011e3d45d25bbe35781217c/multidict-6.7.0-cp311-cp311-macosx_10_9_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "0db4956f82723cc1c270de9c6e799b4c341d327762ec78ef82bb962f79cc07d8", + "url": "https://files.pythonhosted.org/packages/4c/6d/d622322d344f1f053eae47e033b0b3f965af01212de21b10bcf91be991fb/multidict-6.7.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "4c09703000a9d0fa3c3404b27041e574cc7f4df4c6563873246d0e11812a94b6", + "url": "https://files.pythonhosted.org/packages/4d/e1/866a5d77be6ea435711bef2a4291eed11032679b6b28b56b4776ab06ba3e/multidict-6.7.0-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "39f1719f57adbb767ef592a50ae5ebb794220d1188f9ca93de471336401c34d2", + "url": "https://files.pythonhosted.org/packages/63/1b/834ce32a0a97a3b70f86437f685f880136677ac00d8bce0027e9fd9c2db7/multidict-6.7.0-cp311-cp311-musllinux_1_2_i686.whl" + }, + { + "algorithm": "sha256", + "hash": "e3aa16de190d29a0ea1b48253c57d99a68492c8dd8948638073ab9e74dc9410b", + "url": "https://files.pythonhosted.org/packages/6b/03/eaf95bcc2d19ead522001f6a650ef32811aa9e3624ff0ad37c445c7a588c/multidict-6.7.0-cp311-cp311-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "f470f68adc395e0183b92a2f4689264d1ea4b40504a24d9882c27375e6662bb9", + "url": "https://files.pythonhosted.org/packages/75/c4/bbd633980ce6155a28ff04e6a6492dd3335858394d7bb752d8b108708558/multidict-6.7.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "9d14baca2ee12c1a64740d4531356ba50b82543017f3ad6de0deb943c5979abb", + "url": "https://files.pythonhosted.org/packages/78/59/950818e04f91b9c2b95aab3d923d9eabd01689d0dcd889563988e9ea0fd8/multidict-6.7.0-cp311-cp311-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "295a92a76188917c7f99cda95858c822f9e4aae5824246bba9b6b44004ddd0a6", + "url": "https://files.pythonhosted.org/packages/7a/3d/77c79e1934cad2ee74991840f8a0110966d9599b3af95964c0cd79bb905b/multidict-6.7.0-cp311-cp311-musllinux_1_2_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "c6e99d9a65ca282e578dfea819cfa9c0a62b2499d8677392e09feaf305e9e6f5", + "url": "https://files.pythonhosted.org/packages/80/1e/5492c365f222f907de1039b91f922b93fa4f764c713ee858d235495d8f50/multidict-6.7.0.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "db99677b4457c7a5c5a949353e125ba72d62b35f74e26da141530fbb012218a7", + "url": "https://files.pythonhosted.org/packages/a1/cc/d027d9c5a520f3321b65adea289b965e7bcbd2c34402663f482648c716ce/multidict-6.7.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "3e56d780c238f9e1ae66a22d2adf8d16f485381878250db8d496623cd38b22bd", + "url": "https://files.pythonhosted.org/packages/a8/9f/78f8761c2705d4c6d7516faed63c0ebdac569f6db1bef95e0d5218fdc146/multidict-6.7.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "51cb455de290ae462593e5b1cb1118c5c22ea7f0d3620d9940bf695cea5a4bd7", + "url": "https://files.pythonhosted.org/packages/ac/e0/919666a4e4b57fff1b57f279be1c9316e6cdc5de8a8b525d76f6598fefc7/multidict-6.7.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "a048ce45dcdaaf1defb76b2e684f997fb5abf74437b6cb7b22ddad934a964e34", + "url": "https://files.pythonhosted.org/packages/e8/df/ec8a5fd66ea6cd6f525b1fcbb23511b033c3e9bc42b81384834ffa484a62/multidict-6.7.0-cp311-cp311-musllinux_1_2_x86_64.whl" + } + ], + "project_name": "multidict", + "requires_dists": [ + "typing-extensions>=4.1.0; python_version < \"3.11\"" + ], + "requires_python": ">=3.9", + "version": "6.7.0" + }, { "artifacts": [ { @@ -1016,6 +1371,84 @@ "requires_python": ">=3.9", "version": "1.6.0" }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", + "url": "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", + "url": "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", + "url": "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", + "url": "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", + "url": "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", + "url": "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", + "url": "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", + "url": "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", + "url": "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", + "url": "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", + "url": "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", + "url": "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", + "url": "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", + "url": "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl" + } + ], + "project_name": "propcache", + "requires_dists": [], + "requires_python": ">=3.9", + "version": "0.4.1" + }, { "artifacts": [ { @@ -2225,6 +2658,93 @@ "requires_dists": [], "requires_python": ">=3.9", "version": "15.0.1" + }, + { + "artifacts": [ + { + "algorithm": "sha256", + "hash": "1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", + "url": "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl" + }, + { + "algorithm": "sha256", + "hash": "ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", + "url": "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", + "url": "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl" + }, + { + "algorithm": "sha256", + "hash": "3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", + "url": "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", + "url": "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", + "url": "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", + "url": "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl" + }, + { + "algorithm": "sha256", + "hash": "bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", + "url": "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz" + }, + { + "algorithm": "sha256", + "hash": "3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", + "url": "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl" + }, + { + "algorithm": "sha256", + "hash": "669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", + "url": "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl" + }, + { + "algorithm": "sha256", + "hash": "e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", + "url": "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", + "url": "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl" + }, + { + "algorithm": "sha256", + "hash": "3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", + "url": "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl" + }, + { + "algorithm": "sha256", + "hash": "792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", + "url": "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl" + }, + { + "algorithm": "sha256", + "hash": "6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", + "url": "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl" + } + ], + "project_name": "yarl", + "requires_dists": [ + "idna>=2.0", + "multidict>=4.0", + "propcache>=0.2.1" + ], + "requires_python": ">=3.9", + "version": "1.22.0" } ], "marker": null, @@ -2241,6 +2761,8 @@ "requirements": [ "PyGithub==2.8.1", "PyYAML<7.0,>=6.0", + "aiohttp-retry==2.9.1", + "aiohttp==3.12.15", "ansicolors==1.1.8", "beautifulsoup4==4.11.1", "chevron==0.14.0", diff --git a/3rdparty/python/user_reqs.lock.metadata b/3rdparty/python/user_reqs.lock.metadata index 856225c5b74..02d1000d665 100644 --- a/3rdparty/python/user_reqs.lock.metadata +++ b/3rdparty/python/user_reqs.lock.metadata @@ -6,6 +6,8 @@ "generated_with_requirements": [ "PyGithub==2.8.1", "PyYAML<7.0,>=6.0", + "aiohttp-retry==2.9.1", + "aiohttp==3.12.15", "ansicolors==1.1.8", "beautifulsoup4==4.11.1", "chevron==0.14.0", diff --git a/docs/notes/2.31.x.md b/docs/notes/2.31.x.md index 475cd2320dd..0c211f5a7ba 100644 --- a/docs/notes/2.31.x.md +++ b/docs/notes/2.31.x.md @@ -80,6 +80,7 @@ So, this backend provides a simplified set of features from these native packagi - `rpm`: `elfdeps` analyzes ELF metadata and `rpmdeps` adds the requirements to the `.rpm` file. This backend should be platform-agnostic, allowing it to run wherever `pants` can run. To do this, it relies on the [`elfdeps`](https://github.com/python-wheel-build/elfdeps) [📦](https://pypi.org/project/elfdeps/) pure-python package (a "Python implementation of RPM `elfdeps`" with its pure-python dep [`pyelftools`](https://github.com/eliben/pyelftools) [📦](https://pypi.org/project/pyelftools/)) for analyzing `ELF` libraries. +Then, for `deb` packages, this backend queries official [`debian`](https://packages.debian.org/search) or [`ubuntu`](https://packages.ubuntu.com/search) API (over HTTPS) to lookup which package(s) contain required libraries. Using the package search API avoids the local package metadata stores that can only find dependency packages if they are installed (such stores are not just distribution-specific, they are specific to a single release of a distribution). Please provide feedback on this backend [here](https://github.com/pantsbuild/pants/discussions/22396). diff --git a/src/python/pants/backend/nfpm/native_libs/deb/BUILD b/src/python/pants/backend/nfpm/native_libs/deb/BUILD new file mode 100644 index 00000000000..737f35a4b02 --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/BUILD @@ -0,0 +1,17 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +python_sources( + overrides={"rules.py": dict(dependencies=["./search_for_sonames.py"])}, +) + +python_tests( + name="tests", + sources=["*_test.py"], + overrides={"search_for_sonames_integration_test.py": dict(timeout=150)}, +) + +python_test_utils( + name="test_utils", + sources=["test_utils.py"], +) diff --git a/src/python/pants/backend/nfpm/native_libs/deb/__init__.py b/src/python/pants/backend/nfpm/native_libs/deb/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/python/pants/backend/nfpm/native_libs/deb/_constants.py b/src/python/pants/backend/nfpm/native_libs/deb/_constants.py new file mode 100644 index 00000000000..cfaadd2a340 --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/_constants.py @@ -0,0 +1,12 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +"""Constants for use in rule code and script tests.""" + + +DEFAULT_DEB_DISTRO_PACKAGE_SEARCH_URLS: dict[str, str] = { + "debian": "https://packages.debian.org/search", + "ubuntu": "https://packages.ubuntu.com/search", +} diff --git a/src/python/pants/backend/nfpm/native_libs/deb/rules.py b/src/python/pants/backend/nfpm/native_libs/deb/rules.py new file mode 100644 index 00000000000..6434bfc4814 --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/rules.py @@ -0,0 +1,227 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +import importlib.metadata +import json +import logging +import sys +from collections.abc import Iterable, Mapping +from dataclasses import dataclass, replace +from pathlib import PurePath + +from pants.backend.nfpm.native_libs.deb.subsystem import DebSubsystem +from pants.backend.python.util_rules.pex import PexRequest, VenvPexProcess, create_venv_pex +from pants.backend.python.util_rules.pex_environment import PythonExecutable +from pants.backend.python.util_rules.pex_requirements import PexRequirements +from pants.engine.fs import CreateDigest, FileContent +from pants.engine.internals.native_engine import UnionRule +from pants.engine.internals.selectors import concurrently +from pants.engine.intrinsics import create_digest +from pants.engine.process import ProcessResult, execute_process_or_raise +from pants.engine.rules import Rule, collect_rules, implicitly, rule +from pants.init.import_util import find_matching_distributions +from pants.util.logging import LogLevel +from pants.util.resources import read_resource +from pants.version import VERSION + +logger = logging.getLogger(__name__) + +_NATIVE_LIBS_DEB_PACKAGE = "pants.backend.nfpm.native_libs.deb" +_SEARCH_FOR_SONAMES_SCRIPT = "search_for_sonames.py" +_PEX_NAME = "native_libs_deb.pex" + + +@dataclass(frozen=True) +class DebSearchForSonamesRequest: + distro: str + distro_codename: str + debian_arch: str + sonames: tuple[str, ...] + from_best_so_files: bool + + def __init__( + self, + distro: str, + distro_codename: str, + debian_arch: str, + sonames: Iterable[str], + *, + from_best_so_files: bool = False, + ): + object.__setattr__(self, "distro", distro) + object.__setattr__(self, "distro_codename", distro_codename) + object.__setattr__(self, "debian_arch", debian_arch) + object.__setattr__(self, "sonames", tuple(sorted(sonames))) + object.__setattr__(self, "from_best_so_files", from_best_so_files) + + +@dataclass(frozen=True) +class DebPackagesPerSoFile: + so_file: str + packages: tuple[str, ...] + + def __init__(self, so_file: str, packages: Iterable[str]): + object.__setattr__(self, "so_file", so_file) + object.__setattr__(self, "packages", tuple(sorted(packages))) + + +_TYPICAL_LD_PATH_PATTERNS = ( + # platform specific system libs (like libc) get selected first + "/lib/*-linux-*/", + "/usr/lib/*-linux-*/", + # Then look for a generic system libs + "/lib/", + "/usr/lib/", + # Anything else has to be added manually to dependencies. + # These rules cannot use symbols or shlibs metadata to inform package selection. +) + + +@dataclass(frozen=True) +class DebPackagesForSoname: + soname: str + packages_per_so_files: tuple[DebPackagesPerSoFile, ...] + + def __init__(self, soname: str, packages_per_so_files: Iterable[DebPackagesPerSoFile]): + object.__setattr__(self, "soname", soname) + object.__setattr__(self, "packages_per_so_files", tuple(packages_per_so_files)) + + @property + def from_best_so_files(self) -> DebPackagesForSoname: + """Pick best so_files from packages_for_so_files using a simplified ld.so-like algorithm. + + The most preferred is first. This is NOT a recursive match; Only match if direct child of + ld_path_patt dir. Anything that uses a subdir like /usr/lib//lib*.so.* uses rpath to + prefer the app's libs over system libs. If this vastly simplified form of ld.so-style + matching does not select the correct libs, then the package(s) that provide the shared lib + should be added manually to the nfpm requires field. + """ + if len(self.packages_per_so_files) <= 1: # shortcut; no filtering required for 0-1 results. + return self + + remaining = list(self.packages_per_so_files) + + packages_per_so_files = [] + for ld_path_patt in _TYPICAL_LD_PATH_PATTERNS: + for packages_per_so_file in remaining[:]: + if PurePath(packages_per_so_file.so_file).parent.match(ld_path_patt): + packages_per_so_files.append(packages_per_so_file) + remaining.remove(packages_per_so_file) + + return replace(self, packages_per_so_files=tuple(packages_per_so_files)) + + +@dataclass(frozen=True) +class DebPackagesForSonames: + packages_for_sonames: tuple[DebPackagesForSoname, ...] + + @classmethod + def from_dict(cls, raw: Mapping[str, Mapping[str, Iterable[str]]]) -> DebPackagesForSonames: + return cls( + tuple( + DebPackagesForSoname( + soname, + ( + DebPackagesPerSoFile(so_file, packages) + for so_file, packages in files_to_packages.items() + ), + ) + for soname, files_to_packages in raw.items() + ) + ) + + @property + def from_best_so_files(self) -> DebPackagesForSonames: + packages = [] + for packages_for_soname in self.packages_for_sonames: + packages.append(packages_for_soname.from_best_so_files) + return DebPackagesForSonames(tuple(packages)) + + +@rule +async def deb_search_for_sonames( + request: DebSearchForSonamesRequest, + deb_subsystem: DebSubsystem, +) -> DebPackagesForSonames: + script = read_resource(_NATIVE_LIBS_DEB_PACKAGE, _SEARCH_FOR_SONAMES_SCRIPT) + if not script: + raise ValueError( + f"Unable to find source of {_SEARCH_FOR_SONAMES_SCRIPT!r} in {_NATIVE_LIBS_DEB_PACKAGE}" + ) + + script_content = FileContent( + path=_SEARCH_FOR_SONAMES_SCRIPT, content=script, is_executable=True + ) + + # Pull python and requirements versions from the pants venv since that is what the script is tested with. + pants_python = PythonExecutable.fingerprinted( + sys.executable, ".".join(map(str, sys.version_info[:3])).encode("utf8") + ) + distributions_in_pants_venv: list[importlib.metadata.Distribution] = list( + find_matching_distributions(None) + ) + constraints = tuple(f"{dist.name}=={dist.version}" for dist in distributions_in_pants_venv) + requirements = { # requirements (and transitive deps) are constrained to the versions in the pants venv + "aiohttp", + "aiohttp-retry", + "beautifulsoup4", + } + + script_digest, venv_pex = await concurrently( + create_digest(CreateDigest([script_content])), + create_venv_pex( + **implicitly( + PexRequest( + output_filename=_PEX_NAME, + internal_only=True, + python=pants_python, + requirements=PexRequirements( + requirements, + constraints_strings=constraints, + description_of_origin=f"Requirements for {_PEX_NAME}:{_SEARCH_FOR_SONAMES_SCRIPT}", + ), + ) + ) + ), + ) + + distro_package_search_url = deb_subsystem.options.distro_package_search_urls[request.distro] + + # Raising an error means we gave up retrying because the server is unavailable. + result: ProcessResult = await execute_process_or_raise( + **implicitly( + VenvPexProcess( + venv_pex, + argv=( + script_content.path, + f"--user-agent-suffix=pants/{VERSION}", + f"--search-url={distro_package_search_url}", + f"--distro={request.distro}", + f"--distro-codename={request.distro_codename}", + f"--arch={request.debian_arch}", + *request.sonames, + ), + input_digest=script_digest, + description=f"Search deb packages for sonames: {request.sonames}", + level=LogLevel.DEBUG, + ) + ) + ) + + packages = json.loads(result.stdout) + if result.stderr: + logger.warning(result.stderr.decode("utf-8")) + + deb_packages_for_sonames = DebPackagesForSonames.from_dict(packages) + if request.from_best_so_files: + return deb_packages_for_sonames.from_best_so_files + return deb_packages_for_sonames + + +def rules() -> Iterable[Rule | UnionRule]: + return ( + *DebSubsystem.rules(), + *collect_rules(), + ) diff --git a/src/python/pants/backend/nfpm/native_libs/deb/rules_integration_test.py b/src/python/pants/backend/nfpm/native_libs/deb/rules_integration_test.py new file mode 100644 index 00000000000..68b6df190a8 --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/rules_integration_test.py @@ -0,0 +1,73 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +import sys + +import pytest + +from pants.backend.nfpm.native_libs.deb.rules import ( + DebPackagesForSonames, + DebSearchForSonamesRequest, +) +from pants.backend.nfpm.native_libs.deb.rules import rules as native_libs_deb_rules +from pants.backend.nfpm.native_libs.deb.test_utils import TEST_CASES +from pants.backend.python.util_rules import pex_from_targets +from pants.engine.rules import QueryRule +from pants.testutil.rule_runner import RuleRunner + + +@pytest.fixture +def rule_runner() -> RuleRunner: + rule_runner = RuleRunner( + rules=[ + *pex_from_targets.rules(), + *native_libs_deb_rules(), + QueryRule(DebPackagesForSonames, (DebSearchForSonamesRequest,)), + ], + ) + + # The rule builds a pex with wheels for the pants venv. + _py_version = ".".join(map(str, sys.version_info[:3])) + + rule_runner.set_options( + [ + f"--python-interpreter-constraints=['CPython=={_py_version}']", + ], + env_inherit={"PATH", "PYENV_ROOT", "HOME"}, + ) + return rule_runner + + +@pytest.mark.parametrize( + "distro,distro_codename,debian_arch,sonames,expected_raw,expected_raw_from_best_so_files", + TEST_CASES, +) +def test_deb_search_for_sonames_rule( + distro: str, + distro_codename: str, + debian_arch: str, + sonames: tuple[str, ...], + expected_raw: dict[str, dict[str, list[str]]], + expected_raw_from_best_so_files: None | dict[str, dict[str, list[str]]], + rule_runner: RuleRunner, +) -> None: + for from_best_so_files, _expected_raw in ( + (False, expected_raw), + (True, expected_raw_from_best_so_files or expected_raw), + ): + expected = DebPackagesForSonames.from_dict(_expected_raw) + result = rule_runner.request( + DebPackagesForSonames, + [ + DebSearchForSonamesRequest( + distro, + distro_codename, + debian_arch, + sonames, + from_best_so_files=from_best_so_files, + ) + ], + ) + assert result == expected diff --git a/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames.py b/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames.py new file mode 100644 index 00000000000..117da41fe32 --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames.py @@ -0,0 +1,209 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +"""A standalone script that uses asyncio to find packages that contain a SONAME. + +Rule code should run this script as a subprocess in a sandboxed venv. Generally, that venv should +be a subset of the pants venv (using the version of packages that pants depends on). + +WARNING: This script relies on aiohttp and asyncio, so there are probably incompatibilities +with rule code in the pants engine. So, avoid importing this script in rule code. +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import sys +from collections import defaultdict +from collections.abc import Generator, Iterable + +import aiohttp +import aiohttp_retry +from aiohttp.http import SERVER_SOFTWARE as DEFAULT_USER_AGENT +from aiohttp_retry.types import ClientType +from bs4 import BeautifulSoup + + +async def deb_search_for_sonames( + search_url: str, + distro_codename: str, + debian_arch: str, + sonames: Iterable[str], + user_agent: str = DEFAULT_USER_AGENT, +) -> dict[str, dict[str, list[str]]]: + """Given a soname, lookup the deb package that provides it. + + Tools like 'apt-get -S' and 'apt-file' only work for the host's active distro and distro + version. This code, however, should be able to run on any host even non-debian and non-ubuntu + hosts. So, it uses an API call instead of local tooling. + """ + + # tasks are IO bound + async with ( + aiohttp_retry.RetryClient( + retry_options=aiohttp_retry.JitterRetry(attempts=5), + headers={aiohttp.hdrs.USER_AGENT: user_agent}, + # version=aiohttp.HttpVersion11, # aiohttp does not support HTTP/2 (waiting for contribution) + # timeout=aiohttp.ClientTimeout(total=5 * 60, sock_connect=30), + ) as client, + asyncio.TaskGroup() as tg, # client must be before tg in this async with block + ): + tasks = { + soname: tg.create_task( + deb_search_for_soname(client, search_url, distro_codename, debian_arch, soname) + ) + for soname in sonames + } + + # result parsing is CPU bound + packages: defaultdict[str, dict[str, list[str]]] = defaultdict(dict) + for soname, task in tasks.items(): + html_doc = task.result() + for so_file, so_packages in deb_packages_from_html_response(html_doc): + packages[soname][so_file] = list( + so_packages + ) # list makes json serialization more predicatable + + return dict(packages) + + +async def deb_search_for_soname( + http: ClientType, + search_url: str, + distro_codename: str, + debian_arch: str, + soname: str, +) -> str: + """Use API to search for deb packages that contain soname. + + This HTTP+HTML package search API, sadly, does not support any format other than HTML (not JSON, + YAML, etc). + """ + # https://salsa.debian.org/webmaster-team/packages/-/blob/master/SEARCHES?ref_type=heads#L110-136 + query_params = { + "format": "html", # sadly, this API only supports format=html. + "searchon": "contents", + "mode": "exactfilename", # soname should be exact filename. + # mode=="" means find files where `filepath.endswith(keyword)` + # mode=="filename" means find files where `keyword in filename` + # mode=="exactfilename" means find files where `filename==keyword` + "arch": debian_arch, + "suite": distro_codename, + "keywords": soname, + } + + async with http.get(search_url, params=query_params) as response: + # response.status is 200 even if there was an error (like bad distro_codename), + # unless the service is unavailable which happens somewhat frequently. + response.raise_for_status() # That was the last retry. Give up and alert the user. + + # sadly the "API" returns html and does not support other formats. + html_doc = await response.text() + + return html_doc + + +def deb_packages_from_html_response( + html_doc: str, +) -> Generator[tuple[str, tuple[str, ...]]]: + """Extract deb packages from an HTML search response. + + This uses beautifulsoup to parse the search API's HTML responses with logic that is very similar + to the MIT licensed apt-search CLI tool. This does not use apt-search directly because it is not + meant to be a library, and it hardcodes the ubuntu package search URL. https://github.com/david- + haerer/apt-search + """ + + # inspiration from (MIT licensed): + # https://github.com/david-haerer/apt-search/blob/main/apt_search/main.py + # (this script handles more API edge cases than apt-search and creates structured data) + + soup = BeautifulSoup(html_doc, "html.parser") + + # Fail if the API is requesting javascript-based validation + if soup.title.get_text() == "Client Challenge": + raise ValueError("API service is blocking the request") + + # .table means 'search for a tag'. The response should only have one. + # In xmlpath, descending would look like one of these: + # /html/body/div[1]/div[3]/div[2]/table + # /html/body/div[@id="wrapper"]/div[@id="content"]/div[@id="pcontentsres"]/table + results_table = soup.table + + if results_table is None: + # No package(s) found + return + + # results_table is basically (nb: " [amd64] " is only present for arch=any and packages can be a list): + #
+ # + # + # + # + # + # + # + # + # + #
FilePackages
/usr/lib/x86_64-linux-gnu/libldap-2.5.so.0libldap-2.5.0 [amd64]
/usr/sbin/dnsmasqdnsmasq-base, dnsmasq-base-lua
+ # But, html is semi-structured, so assume that it can be in a broken state. + + for row in results_table.find_all("tr"): + cells = tuple(row.find_all("td")) + if len(cells) < 2: + # ignore malformed rows with missing cell(s). + continue + file_cell, pkgs_cell = cells[:2] + file_text = file_cell.get_text(strip=True) + packages = [pkg_a.get_text(strip=True) for pkg_a in pkgs_cell.find_all("a")] + yield file_text, tuple(packages) + + return + + +def main() -> int: + arg_parser = argparse.ArgumentParser() + arg_parser.add_argument("--user-agent-suffix") + arg_parser.add_argument("--search-url", default="https://packages.debian.org/search") + arg_parser.add_argument("--distro", default="debian") + arg_parser.add_argument("--distro-codename", required=True) + arg_parser.add_argument("--arch", default="amd64") + arg_parser.add_argument("sonames", nargs="+") + options = arg_parser.parse_args() + + user_agent_suffix = options.user_agent_suffix + user_agent = ( + DEFAULT_USER_AGENT if not user_agent_suffix else f"{DEFAULT_USER_AGENT} {user_agent_suffix}" + ) + + packages = asyncio.get_event_loop().run_until_complete( + deb_search_for_sonames( + search_url=options.search_url, + distro_codename=options.distro_codename, + debian_arch=options.arch, + sonames=tuple(options.sonames), + user_agent=user_agent, + ) + ) + + if not packages: + # The search API returns 200 even if no results were found (which is not an error). + # If the service is unavailable, an error should be raised before getting here. + print("{}") + print( + f"No {options.distro} {options.distro_codename} ({options.arch}) packages" + f" found for sonames: {options.sonames}", + file=sys.stderr, + ) + # If needed, add a flag to this script to say "fail if no results found". + return 0 + + print(json.dumps(packages, indent=None, separators=(",", ":"))) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames_integration_test.py b/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames_integration_test.py new file mode 100644 index 00000000000..19a0a15160f --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames_integration_test.py @@ -0,0 +1,29 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +from typing import Any + +import pytest + +from pants.backend.nfpm.native_libs.deb._constants import DEFAULT_DEB_DISTRO_PACKAGE_SEARCH_URLS +from pants.backend.nfpm.native_libs.deb.test_utils import TEST_CASES + +# The relative import emphasizes that `search_for_sonames` is a standalone script that runs +# from a sandbox root (thus avoiding a dependency on the pants code structure). +from .search_for_sonames import deb_search_for_sonames + + +@pytest.mark.parametrize("distro,distro_codename,debian_arch,sonames,expected,_", TEST_CASES) +async def test_deb_search_for_sonames( + distro: str, + distro_codename: str, + debian_arch: str, + sonames: tuple[str, ...], + expected: dict[str, dict[str, list[str]]], + _: Any, # unused. This is for the rule integration test. +): + search_url = DEFAULT_DEB_DISTRO_PACKAGE_SEARCH_URLS[distro] + result = await deb_search_for_sonames(search_url, distro_codename, debian_arch, sonames) + assert result == expected diff --git a/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames_test.py b/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames_test.py new file mode 100644 index 00000000000..275bbd5be1b --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/search_for_sonames_test.py @@ -0,0 +1,42 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +# The relative import emphasizes that `search_for_sonames` is a standalone script that runs +# from a sandbox root (thus avoiding a dependency on the pants code structure). +from .search_for_sonames import deb_packages_from_html_response + +# simplified for readability and to keep it focused +SAMPLE_HTML_RESPONSE = """ + + + + Debian -- Package Contents Search Results -- libldap-2.5.so.0 + + + +
...
+ + + + + + + + + + +
FilePackages
/usr/lib/x86_64-linux-gnu/libldap-2.5.so.0libldap-2.5.0 [amd64]
/usr/sbin/dnsmasqdnsmasq-base, dnsmasq-base-lua
+
...
+ + +""" + + +def test_deb_packages_from_html_response(): + results = list(deb_packages_from_html_response(SAMPLE_HTML_RESPONSE)) + assert results == [ + ("/usr/lib/x86_64-linux-gnu/libldap-2.5.so.0", ("libldap-2.5.0",)), + ("/usr/sbin/dnsmasq", ("dnsmasq-base", "dnsmasq-base-lua")), + ] diff --git a/src/python/pants/backend/nfpm/native_libs/deb/subsystem.py b/src/python/pants/backend/nfpm/native_libs/deb/subsystem.py new file mode 100644 index 00000000000..82171aed5c9 --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/subsystem.py @@ -0,0 +1,32 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +from pants.backend.nfpm.native_libs.deb._constants import DEFAULT_DEB_DISTRO_PACKAGE_SEARCH_URLS +from pants.option.option_types import DictOption +from pants.option.subsystem import Subsystem +from pants.util.strutil import help_text + + +class DebSubsystem(Subsystem): + options_scope = "deb" # not "debian" to avoid conflicting with debian backend + help = "Options for deb scripts in the nfpm.native_libs backend." + + distro_package_search_urls = DictOption[str]( + default=DEFAULT_DEB_DISTRO_PACKAGE_SEARCH_URLS, + help=help_text( + """ + A mapping of distro names to package search URLs. + + This is used when inspecting packaged native_libs to inject nfpm package deps. + + The key is a distro name and should be lowercase. + + Each value is a fully-qualified URL with scheme (`https://`), domain, and path + (path is typically `/search`). The URL must point to an instance of the debian + package search service, returning the API results in HTML format. + """ + ), + advanced=True, + ) diff --git a/src/python/pants/backend/nfpm/native_libs/deb/test_utils.py b/src/python/pants/backend/nfpm/native_libs/deb/test_utils.py new file mode 100644 index 00000000000..0fe935b926a --- /dev/null +++ b/src/python/pants/backend/nfpm/native_libs/deb/test_utils.py @@ -0,0 +1,189 @@ +# Copyright 2025 Pants project contributors (see CONTRIBUTORS.md). +# Licensed under the Apache License, Version 2.0 (see LICENSE). + +from __future__ import annotations + +import pytest + +_libldap_soname = "libldap-2.5.so.0" +_libldap_so_file = "/usr/lib/{}-linux-gnu/" + _libldap_soname +_libldap_pkg = "libldap-2.5-0" + +_libc6_soname = "libc.so.6" + + +def _libc6_pkgs(_arch: str, prof: bool = True) -> dict[str, list[str]]: + pkgs = {} + if prof: + pkgs[f"/lib/libc6-prof/{_arch}-linux-gnu/{_libc6_soname}"] = ["libc6-prof"] + pkgs[f"/lib/{_arch}-linux-gnu/{_libc6_soname}"] = ["libc6"] + return dict(sorted(pkgs.items())) + + +_libc6_pkgs_amd64 = { # only x86_64 not aarch64 + "/lib32/" + _libc6_soname: ["libc6-i386"], + "/libx32/" + _libc6_soname: ["libc6-x32"], +} +_libc6_cross_pkgs = { + f"/usr/{cross_machine}-linux-{cross_os_lib}/lib{cross_bits}/{_libc6_soname}": [ + f"libc6-{cross_arch}-cross" + ] + for cross_machine, cross_os_lib, cross_bits, cross_arch in [ + ("aarch64", "gnu", "", "arm64"), + ("arm", "gnueabi", "", "armel"), + ("arm", "gnueabihf", "", "armhf"), + ("hppa", "gnu", "", "hppa"), + ("i686", "gnu", "", "i386"), + ("i686", "gnu", "64", "amd64-i386"), + ("i686", "gnu", "x32", "x32-i386"), + ("m68k", "gnu", "", "m68k"), + ("mips", "gnu", "", "mips"), + ("mips", "gnu", "32", "mipsn32-mips"), + ("mips", "gnu", "64", "mips64-mips"), + ("mips64", "gnuabi64", "", "mips64"), + ("mips64", "gnuabi64", "32", "mipsn32-mips64"), + ("mips64", "gnuabi64", "o32", "mips32-mips64"), + ("mips64", "gnuabin32", "", "mipsn32"), + ("mips64", "gnuabin32", "64", "mips64-mipsn32"), + ("mips64", "gnuabin32", "o32", "mips32-mipsn32"), + ("mips64el", "gnuabi64", "", "mips64el"), + ("mips64el", "gnuabi64", "32", "mipsn32-mips64el"), + ("mips64el", "gnuabi64", "o32", "mips32-mips64el"), + ("mips64el", "gnuabin32", "", "mipsn32el"), + ("mips64el", "gnuabin32", "64", "mips64-mipsn32el"), + ("mips64el", "gnuabin32", "o32", "mips32-mipsn32el"), + ("mipsel", "gnu", "", "mipsel"), + ("mipsel", "gnu", "32", "mipsn32-mipsel"), + ("mipsel", "gnu", "64", "mips64-mipsel"), + ("mipsisa32r6", "gnu", "", "mipsr6"), + ("mipsisa32r6", "gnu", "32", "mipsn32-mipsr6"), + ("mipsisa32r6", "gnu", "64", "mips64-mipsr6"), + ("mipsisa32r6el", "gnu", "", "mipsr6el"), + ("mipsisa32r6el", "gnu", "32", "mipsn32-mipsr6el"), + ("mipsisa32r6el", "gnu", "64", "mips64-mipsr6el"), + ("mipsisa64r6", "gnuabi64", "", "mips64r6"), + ("mipsisa64r6", "gnuabi64", "32", "mipsn32-mips64r6"), + ("mipsisa64r6", "gnuabi64", "o32", "mips32-mips64r6"), + ("mipsisa64r6", "gnuabin32", "", "mipsn32r6"), + ("mipsisa64r6", "gnuabin32", "64", "mips64-mipsn32r6"), + ("mipsisa64r6", "gnuabin32", "o32", "mips32-mipsn32r6"), + ("mipsisa64r6el", "gnuabi64", "", "mips64r6el"), + ("mipsisa64r6el", "gnuabi64", "32", "mipsn32-mips64r6el"), + ("mipsisa64r6el", "gnuabi64", "o32", "mips32-mips64r6el"), + ("mipsisa64r6el", "gnuabin32", "", "mipsn32r6el"), + ("mipsisa64r6el", "gnuabin32", "64", "mips64-mipsn32r6el"), + ("mipsisa64r6el", "gnuabin32", "o32", "mips32-mipsn32r6el"), + ("powerpc", "gnu", "", "powerpc"), + ("powerpc", "gnu", "64", "ppc64-powerpc"), + ("powerpc64", "gnu", "", "ppc64"), + ("powerpc64", "gnu", "32", "powerpc-ppc64"), + ("powerpc64le", "gnu", "", "ppc64el"), + ("riscv64", "gnu", "", "riscv64"), + ("s390x", "gnu", "", "s390x"), + ("s390x", "gnu", "32", "s390-s390x"), + ("sh4", "gnu", "", "sh4"), + ("sparc64", "gnu", "", "sparc64"), + ("sparc64", "gnu", "32", "sparc-sparc64"), + ("x86_64", "gnu", "", "amd64"), + ("x86_64", "gnu", "32", "i386-amd64"), + ("x86_64", "gnu", "x32", "x32-amd64"), + ("x86_64", "gnux32", "", "x32"), + ("x86_64", "gnux32", "32", "i386-x32"), + ("x86_64", "gnux32", "64", "amd64-x32"), + ] +} + +TEST_CASES = ( + # debian is blocking non-javascript-enabled search requests + # pytest.param( + # "debian", + # "bookworm", + # "amd64", + # (_libldap_soname,), + # {_libldap_soname: {_libldap_so_file.format("x86_64"): [_libldap_pkg]}}, + # None, # from_best_so_files is the same result + # id="debian-amd64-libldap", + # ), + # pytest.param( + # "debian", + # "bookworm", + # "arm64", + # (_libldap_soname,), + # {_libldap_soname: {_libldap_so_file.format("aarch64"): [_libldap_pkg]}}, + # None, # from_best_so_files is the same result + # id="debian-arm64-libldap", + # ), + pytest.param( + "ubuntu", + "jammy", + "amd64", + (_libldap_soname,), + {_libldap_soname: {_libldap_so_file.format("x86_64"): [_libldap_pkg]}}, + None, # from_best_so_files is the same result + id="ubuntu-amd64-libldap", + ), + pytest.param( + "ubuntu", + "jammy", + "arm64", + (_libldap_soname,), + {_libldap_soname: {_libldap_so_file.format("aarch64"): [_libldap_pkg]}}, + None, # from_best_so_files is the same result + id="ubuntu-arm64-libldap", + ), + pytest.param( + "ubuntu", "foobar", "amd64", (_libldap_soname,), {}, None, id="bad-distro_codename" + ), + pytest.param("ubuntu", "jammy", "foobar", (_libldap_soname,), {}, None, id="bad-debian_arch"), + pytest.param("ubuntu", "jammy", "amd64", ("foobarbaz-9.9.so.9",), {}, None, id="bad-soname"), + pytest.param( + "ubuntu", + "jammy", + "amd64", + ("libcurl.so",), # the search api returns a table like this: + # ------------------------------------------- | ----------------------------------------------------------- | + # File | Packages | + # ------------------------------------------- | ----------------------------------------------------------- | + # /usr/lib/cupt4-2/downloadmethods/libcurl.so | libcupt4-2-downloadmethod-curl | + # /usr/lib/x86_64-linux-gnu/libcurl.so | libcurl4-gnutls-dev, libcurl4-nss-dev, libcurl4-openssl-dev | + # ------------------------------------------- | ----------------------------------------------------------- | + { + "libcurl.so": { + "/usr/lib/cupt4-2/downloadmethods/libcurl.so": ["libcupt4-2-downloadmethod-curl"], + "/usr/lib/x86_64-linux-gnu/libcurl.so": [ + "libcurl4-gnutls-dev", + "libcurl4-nss-dev", + "libcurl4-openssl-dev", + ], + } + }, + { # from_best_so_files is NOT the same result + "libcurl.so": { + "/usr/lib/x86_64-linux-gnu/libcurl.so": [ + "libcurl4-gnutls-dev", + "libcurl4-nss-dev", + "libcurl4-openssl-dev", + ], + } + }, + id="same-file-in-multiple-packages", + ), + pytest.param( + "ubuntu", + "jammy", + "amd64", + (_libc6_soname,), + {_libc6_soname: _libc6_pkgs("x86_64") | _libc6_pkgs_amd64 | _libc6_cross_pkgs}, + {_libc6_soname: _libc6_pkgs("x86_64", prof=False)}, + id="ubuntu-amd64-libc6", + ), + pytest.param( + "ubuntu", + "jammy", + "arm64", + (_libc6_soname,), + {_libc6_soname: _libc6_pkgs("aarch64") | _libc6_cross_pkgs}, + {_libc6_soname: _libc6_pkgs("aarch64", prof=False)}, + id="ubuntu-arm64-libc6", + ), +) diff --git a/src/python/pants/backend/nfpm/native_libs/rules.py b/src/python/pants/backend/nfpm/native_libs/rules.py index cfc7a8a83f1..5fd14ffccca 100644 --- a/src/python/pants/backend/nfpm/native_libs/rules.py +++ b/src/python/pants/backend/nfpm/native_libs/rules.py @@ -8,6 +8,7 @@ from pants.backend.nfpm.field_sets import NfpmRpmPackageFieldSet from pants.backend.nfpm.fields.rpm import NfpmRpmDependsField, NfpmRpmProvidesField +from pants.backend.nfpm.native_libs.deb.rules import rules as deb_rules from pants.backend.nfpm.native_libs.elfdeps.rules import RequestPexELFInfo, elfdeps_analyze_pex from pants.backend.nfpm.native_libs.elfdeps.rules import rules as elfdeps_rules from pants.backend.nfpm.util_rules.contents import ( @@ -117,6 +118,7 @@ async def inject_native_libs_dependencies_in_package_fields( def rules() -> Iterable[Rule | UnionRule]: return ( + *deb_rules(), *elfdeps_rules(), *collect_rules(), UnionRule(InjectNfpmPackageFieldsRequest, NativeLibsNfpmPackageFieldsRequest),