mirror of
https://github.com/pyenv/pyenv.git
synced 2026-09-10 07:36:16 -04:00
Compare commits
105 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef4905cd81 | ||
|
|
88e9ab13e8 | ||
|
|
798a1ad603 | ||
|
|
fb7f2071fb | ||
|
|
ef7a9f5d14 | ||
|
|
265b0dcefa | ||
|
|
2548d53706 | ||
|
|
c293de3650 | ||
|
|
bdfc6b03f3 | ||
|
|
8c46508bd0 | ||
|
|
a00e0bc475 | ||
|
|
981559003f | ||
|
|
4733cca31b | ||
|
|
ef2fb22235 | ||
|
|
97900a4aa4 | ||
|
|
ecf9a7f9e6 | ||
|
|
495550afc8 | ||
|
|
57f1df294b | ||
|
|
14830b18bf | ||
|
|
032e7af407 | ||
|
|
b0cff001ff | ||
|
|
234a87e400 | ||
|
|
50fbc8c6cc | ||
|
|
0f16606e4f | ||
|
|
7c2226386b | ||
|
|
3a98f8c1c3 | ||
|
|
f99638cb82 | ||
|
|
3b9604c093 | ||
|
|
920052eb0b | ||
|
|
cb20e375d3 | ||
|
|
f11926a274 | ||
|
|
0fc5dfe630 | ||
|
|
2bcc7d8fc4 | ||
|
|
2bd4f6c4da | ||
|
|
8771e721c1 | ||
|
|
d92b6e1bbe | ||
|
|
9962f8cb39 | ||
|
|
f9ffb73a7e | ||
|
|
bd7c969e7f | ||
|
|
4a73bd17e2 | ||
|
|
2c1f405180 | ||
|
|
e234812f46 | ||
|
|
1a591b3963 | ||
|
|
48e70e1fe9 | ||
|
|
195b9fe940 | ||
|
|
b7e7adf4ec | ||
|
|
a6ef1e70ba | ||
|
|
5a31fea21b | ||
|
|
17be308600 | ||
|
|
9d6cf587b7 | ||
|
|
1cc9e770ae | ||
|
|
0e2e214f56 | ||
|
|
340348d98d | ||
|
|
2d9a224e6c | ||
|
|
da172db42d | ||
|
|
b0a64af8cc | ||
|
|
9e4d1ca95a | ||
|
|
b9e165271e | ||
|
|
3088203e33 | ||
|
|
8f70d418bb | ||
|
|
bdf8d254fe | ||
|
|
f5915fc866 | ||
|
|
84e0f3a887 | ||
|
|
7126870320 | ||
|
|
26dd5d9dda | ||
|
|
bac39a0ef8 | ||
|
|
bec9a562a5 | ||
|
|
ae9e6594d6 | ||
|
|
16c7cc6de4 | ||
|
|
017ad4a573 | ||
|
|
6188e9efa9 | ||
|
|
286a167cf0 | ||
|
|
135adbb192 | ||
|
|
b3a3e702f1 | ||
|
|
958e3aca95 | ||
|
|
74c6efe2f4 | ||
|
|
95df7dbc7b | ||
|
|
e4c462dc70 | ||
|
|
790bedd821 | ||
|
|
19f4c7cf20 | ||
|
|
46cc3e552b | ||
|
|
8f68aab443 | ||
|
|
a27d2723d6 | ||
|
|
4c78a0dd50 | ||
|
|
91aacc5dbf | ||
|
|
3974647f0f | ||
|
|
ebd788d08c | ||
|
|
4b77d83d9d | ||
|
|
933d0aaf1d | ||
|
|
09dce106a4 | ||
|
|
b52a8e3f52 | ||
|
|
f122a9d764 | ||
|
|
3323ba3ed9 | ||
|
|
b43c9e5d42 | ||
|
|
532b659ca2 | ||
|
|
7565df6eab | ||
|
|
e55613a267 | ||
|
|
6c0c5cfa96 | ||
|
|
cf6f3c3200 | ||
|
|
2f8cb2cf1f | ||
|
|
b8d8dee191 | ||
|
|
ba9099392a | ||
|
|
d428be59ec | ||
|
|
dffc750fa2 | ||
|
|
f46b669f1d |
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -3,9 +3,6 @@ Make sure you have checked all steps below.
|
||||||
### Prerequisite
|
### Prerequisite
|
||||||
* [ ] Please consider implementing the feature as a hook script or plugin as a first step.
|
* [ ] Please consider implementing the feature as a hook script or plugin as a first step.
|
||||||
* pyenv has some powerful support for plugins and hook scripts. Please refer to [Authoring plugins](https://github.com/pyenv/pyenv/wiki/Authoring-plugins) for details and try to implement it as a plugin if possible.
|
* pyenv has some powerful support for plugins and hook scripts. Please refer to [Authoring plugins](https://github.com/pyenv/pyenv/wiki/Authoring-plugins) for details and try to implement it as a plugin if possible.
|
||||||
* [ ] Please consider contributing the patch upstream to [rbenv](https://github.com/rbenv/rbenv), since we have borrowed most of the code from that project.
|
|
||||||
* We occasionally import the changes from rbenv. In general, you can expect changes made in rbenv will be imported to pyenv too, eventually.
|
|
||||||
* Generally speaking, we prefer not to make changes in the core in order to keep compatibility with rbenv.
|
|
||||||
* [ ] My PR addresses the following pyenv issue (if any)
|
* [ ] My PR addresses the following pyenv issue (if any)
|
||||||
- Closes https://github.com/pyenv/pyenv/issues/XXXX
|
- Closes https://github.com/pyenv/pyenv/issues/XXXX
|
||||||
|
|
||||||
|
|
|
||||||
44
.github/actions/build-python/action.yml
vendored
Normal file
44
.github/actions/build-python/action.yml
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
name: Build a Python version
|
||||||
|
description: >
|
||||||
|
Install python-build's system dependencies for the current runner's OS, build
|
||||||
|
and check the requested version.
|
||||||
|
inputs:
|
||||||
|
python-version:
|
||||||
|
description: A version as accepted by `pyenv install`.
|
||||||
|
required: true
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- shell: bash
|
||||||
|
run: |
|
||||||
|
#envvars
|
||||||
|
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
||||||
|
echo "PYENV_ROOT=$PYENV_ROOT" >> $GITHUB_ENV
|
||||||
|
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
||||||
|
# The two OSes differ only in this step.
|
||||||
|
- if: runner.os == 'macOS'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
#prerequisites
|
||||||
|
brew install openssl readline sqlite3 xz tcl-tk@8 libb2 zstd
|
||||||
|
- if: runner.os == 'Linux'
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
#prerequisites
|
||||||
|
pyenv install-prerequisites
|
||||||
|
- shell: bash
|
||||||
|
run: |
|
||||||
|
#build
|
||||||
|
pyenv --debug install ${{ inputs.python-version }} && rc=$? || rc=$?
|
||||||
|
if [[ $rc -ne 0 ]]; then echo config.log:; cat "${TMPDIR:-/tmp}"/python-build*/*/config.log; false; fi
|
||||||
|
pyenv global ${{ inputs.python-version }}
|
||||||
|
pyenv rehash
|
||||||
|
- shell: bash
|
||||||
|
run: |
|
||||||
|
#print version
|
||||||
|
python --version
|
||||||
|
python -m pip --version
|
||||||
|
- shell: python # Prove that actual Python == expected Python
|
||||||
|
env:
|
||||||
|
EXPECTED_PYTHON: ${{ inputs.python-version }}
|
||||||
|
run: import os, sys ; assert sys.version.startswith(os.getenv("EXPECTED_PYTHON"))
|
||||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
|
|
@ -4,6 +4,8 @@ updates:
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: "monthly"
|
interval: "monthly"
|
||||||
|
cooldown:
|
||||||
|
default-days: 7
|
||||||
groups:
|
groups:
|
||||||
github-actions:
|
github-actions:
|
||||||
patterns:
|
patterns:
|
||||||
|
|
|
||||||
364
.github/scripts/generate_release_notes_sponsors.py
vendored
Executable file
364
.github/scripts/generate_release_notes_sponsors.py
vendored
Executable file
|
|
@ -0,0 +1,364 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate a "Sponsors since <date>" section for release notes.
|
||||||
|
|
||||||
|
Queries GitHub Sponsors and OpenCollective for new sponsors since the latest
|
||||||
|
pyenv release or one month ago, whichever is longer. The output is Markdown
|
||||||
|
suitable for GitHub Releases.
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
* Python 3.8+
|
||||||
|
* The ``gh`` CLI authenticated with the ``read:user`` scope.
|
||||||
|
* Network access to https://opencollective.com.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import calendar
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import pathlib
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import typing
|
||||||
|
import urllib.error
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
|
||||||
|
GITHUB_ORG = "pyenv"
|
||||||
|
OPENCOLLECTIVE_MEMBERS_URL = "https://opencollective.com/pyenv/members.json"
|
||||||
|
|
||||||
|
|
||||||
|
class SponsorDataError(RuntimeError):
|
||||||
|
"""Raised when a sponsors data source cannot be queried or parsed."""
|
||||||
|
|
||||||
|
|
||||||
|
def parse_date(value: str) -> datetime.date:
|
||||||
|
return datetime.datetime.fromisoformat(value).date()
|
||||||
|
|
||||||
|
|
||||||
|
def one_month_ago(today: typing.Optional[datetime.date] = None) -> datetime.date:
|
||||||
|
today = today or datetime.date.today()
|
||||||
|
year = today.year
|
||||||
|
month = today.month - 1
|
||||||
|
if month == 0:
|
||||||
|
year -= 1
|
||||||
|
month = 12
|
||||||
|
try:
|
||||||
|
return datetime.date(year, month, today.day)
|
||||||
|
except ValueError:
|
||||||
|
last_day = calendar.monthrange(year, month)[1]
|
||||||
|
return datetime.date(year, month, last_day)
|
||||||
|
|
||||||
|
|
||||||
|
def latest_release_date() -> datetime.date:
|
||||||
|
"""Return the publish date of the latest GitHub release."""
|
||||||
|
latest_release_error = None
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["gh", "api", f"repos/{GITHUB_ORG}/{GITHUB_ORG}/releases/latest"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
published = data["published_at"].replace("Z", "+00:00")
|
||||||
|
return datetime.datetime.fromisoformat(published).date()
|
||||||
|
except (
|
||||||
|
subprocess.CalledProcessError,
|
||||||
|
OSError,
|
||||||
|
json.JSONDecodeError,
|
||||||
|
KeyError,
|
||||||
|
) as exc:
|
||||||
|
latest_release_error = exc
|
||||||
|
|
||||||
|
try:
|
||||||
|
tag = subprocess.run(
|
||||||
|
["git", "describe", "--tags", "--abbrev=0"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
).stdout.strip()
|
||||||
|
date_str = subprocess.run(
|
||||||
|
["git", "log", "-1", "--format=%cI", tag],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
).stdout.strip()
|
||||||
|
return datetime.datetime.fromisoformat(date_str).date()
|
||||||
|
except (subprocess.CalledProcessError, OSError) as exc:
|
||||||
|
detail = ""
|
||||||
|
if latest_release_error is not None:
|
||||||
|
detail = f" GitHub release lookup failed first: {latest_release_error}."
|
||||||
|
raise SponsorDataError(
|
||||||
|
"Could not determine the latest release date. "
|
||||||
|
"Pass --since explicitly or run from a clone with release tags."
|
||||||
|
f"{detail}"
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
|
||||||
|
def compute_since_date(explicit_since: typing.Optional[datetime.date]) -> datetime.date:
|
||||||
|
if explicit_since is not None:
|
||||||
|
return explicit_since
|
||||||
|
return min(latest_release_date(), one_month_ago())
|
||||||
|
|
||||||
|
|
||||||
|
def github_sponsors(since: datetime.date) -> typing.List[typing.Dict]:
|
||||||
|
"""Return GitHub Sponsors created on or after *since*."""
|
||||||
|
query = """
|
||||||
|
query($org: String!, $after: String) {
|
||||||
|
organization(login: $org) {
|
||||||
|
sponsorshipsAsMaintainer(
|
||||||
|
first: 100,
|
||||||
|
after: $after,
|
||||||
|
activeOnly: false,
|
||||||
|
orderBy: {field: CREATED_AT, direction: DESC}
|
||||||
|
) {
|
||||||
|
pageInfo {
|
||||||
|
hasNextPage
|
||||||
|
endCursor
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
createdAt
|
||||||
|
sponsorEntity {
|
||||||
|
... on User { login, name }
|
||||||
|
... on Organization { login, name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
since_dt = datetime.datetime.combine(
|
||||||
|
since, datetime.time.min, tzinfo=datetime.timezone.utc
|
||||||
|
)
|
||||||
|
sponsors = []
|
||||||
|
cursor = None
|
||||||
|
while True:
|
||||||
|
command = [
|
||||||
|
"gh",
|
||||||
|
"api",
|
||||||
|
"graphql",
|
||||||
|
"-F",
|
||||||
|
f"org={GITHUB_ORG}",
|
||||||
|
"-f",
|
||||||
|
f"query={query}",
|
||||||
|
]
|
||||||
|
if cursor is not None:
|
||||||
|
command.extend(["-F", f"after={cursor}"])
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
command,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
detail = result.stderr.strip() or result.stdout.strip() or "no output"
|
||||||
|
raise SponsorDataError(
|
||||||
|
"GitHub Sponsors query failed. "
|
||||||
|
"Check that `gh auth status` shows access to the pyenv org "
|
||||||
|
"and that the token has the scopes required to read sponsorships. "
|
||||||
|
f"`gh api graphql` exited {result.returncode}: {detail}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
"GitHub Sponsors query returned invalid JSON."
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
if "errors" in data:
|
||||||
|
raise SponsorDataError(
|
||||||
|
f"GitHub Sponsors query returned errors: {data['errors']}"
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
sponsorships = data["data"]["organization"]["sponsorshipsAsMaintainer"]
|
||||||
|
except (TypeError, KeyError) as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
"GitHub Sponsors query returned an unexpected response shape."
|
||||||
|
) from exc
|
||||||
|
try:
|
||||||
|
nodes = sponsorships["nodes"]
|
||||||
|
page_info = sponsorships["pageInfo"]
|
||||||
|
except (TypeError, KeyError) as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
"GitHub Sponsors query returned incomplete pagination data."
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
for node in nodes:
|
||||||
|
try:
|
||||||
|
created = datetime.datetime.fromisoformat(
|
||||||
|
node["createdAt"].replace("Z", "+00:00")
|
||||||
|
)
|
||||||
|
entity = node["sponsorEntity"]
|
||||||
|
login = entity["login"]
|
||||||
|
except (AttributeError, KeyError, TypeError, ValueError) as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
"GitHub Sponsors query returned an unexpected sponsor record."
|
||||||
|
) from exc
|
||||||
|
if created < since_dt:
|
||||||
|
return sponsors
|
||||||
|
sponsors.append({
|
||||||
|
"login": login,
|
||||||
|
"name": entity.get("name") or login,
|
||||||
|
})
|
||||||
|
|
||||||
|
try:
|
||||||
|
has_next_page = page_info["hasNextPage"]
|
||||||
|
cursor = page_info["endCursor"]
|
||||||
|
except (TypeError, KeyError) as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
"GitHub Sponsors query returned incomplete pagination data."
|
||||||
|
) from exc
|
||||||
|
if not has_next_page:
|
||||||
|
return sponsors
|
||||||
|
|
||||||
|
|
||||||
|
def opencollective_sponsors(since: datetime.date, data: typing.Union[str, None]) -> typing.List[typing.Dict]:
|
||||||
|
"""Return OpenCollective backers active on or after *since*."""
|
||||||
|
if data is None:
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f"{OPENCOLLECTIVE_MEMBERS_URL}?limit=1000",
|
||||||
|
headers={"User-Agent": f"{GITHUB_ORG}/release-notes-sponsors"},
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
|
data = resp.read()
|
||||||
|
except urllib.error.HTTPError as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
f"OpenCollective sponsors query failed for {OPENCOLLECTIVE_MEMBERS_URL}: "
|
||||||
|
f"HTTP {exc.code} {exc.reason}"
|
||||||
|
) from exc
|
||||||
|
except urllib.error.URLError as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
f"OpenCollective sponsors query failed for {OPENCOLLECTIVE_MEMBERS_URL}: "
|
||||||
|
f"{exc.reason}"
|
||||||
|
) from exc
|
||||||
|
try:
|
||||||
|
members = json.loads(data)
|
||||||
|
except json.JSONDecodeError as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
"OpenCollective sponsors query returned invalid JSON."
|
||||||
|
) from exc
|
||||||
|
|
||||||
|
since_dt = datetime.datetime.combine(since, datetime.time.min)
|
||||||
|
sponsors = []
|
||||||
|
for member in members:
|
||||||
|
if member.get("role") != "BACKER":
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
last_transaction_at = datetime.datetime.strptime(member["lastTransactionAt"], "%Y-%m-%d %H:%M")
|
||||||
|
profile = member["profile"].rstrip("/")
|
||||||
|
except (KeyError, TypeError, ValueError) as exc:
|
||||||
|
raise SponsorDataError(
|
||||||
|
"OpenCollective sponsors query returned an unexpected member record."
|
||||||
|
) from exc
|
||||||
|
if last_transaction_at < since_dt:
|
||||||
|
continue
|
||||||
|
slug = profile.split("/")[-1]
|
||||||
|
if slug == "github-sponsors":
|
||||||
|
# Listed separately under GitHub Sponsors.
|
||||||
|
continue
|
||||||
|
sponsors.append({
|
||||||
|
"name": member.get("name") or slug,
|
||||||
|
"profile": profile,
|
||||||
|
})
|
||||||
|
return sponsors
|
||||||
|
|
||||||
|
|
||||||
|
def render(
|
||||||
|
since: datetime.date,
|
||||||
|
gh_sponsors: typing.List[typing.Dict],
|
||||||
|
oc_sponsors: typing.List[typing.Dict],
|
||||||
|
) -> str:
|
||||||
|
lines = [f"## Sponsors since {since.isoformat()}", ""]
|
||||||
|
|
||||||
|
if gh_sponsors:
|
||||||
|
lines.append("### GitHub Sponsors")
|
||||||
|
for sponsor in gh_sponsors:
|
||||||
|
lines.append(
|
||||||
|
markdown_link(
|
||||||
|
sponsor["name"],
|
||||||
|
f"https://github.com/{sponsor['login']}",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
if oc_sponsors:
|
||||||
|
lines.append("### Open Collective")
|
||||||
|
for sponsor in oc_sponsors:
|
||||||
|
lines.append(markdown_link(sponsor["name"], sponsor["profile"]))
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
if not gh_sponsors and not oc_sponsors:
|
||||||
|
lines.append("*No new sponsors in this period.*")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
def markdown_link(text: str, url: str) -> str:
|
||||||
|
escaped_text = escape_markdown_text(text)
|
||||||
|
escaped_url = url.replace(")", "%29")
|
||||||
|
return f"- [{escaped_text}]({escaped_url})"
|
||||||
|
|
||||||
|
|
||||||
|
def escape_markdown_text(text: str) -> str:
|
||||||
|
escaped = text.replace("\\", "\\\\")
|
||||||
|
for char in r"`*_{}[]()#+-.!|>":
|
||||||
|
escaped = escaped.replace(char, f"\\{char}")
|
||||||
|
return escaped
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="Generate a sponsors section for GitHub release notes."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--since",
|
||||||
|
type=parse_date,
|
||||||
|
metavar="YYYY-MM-DD",
|
||||||
|
help="Include sponsors created on or after this date.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--output",
|
||||||
|
metavar="FILE",
|
||||||
|
help="Write the section to FILE instead of stdout.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--skip-opencollective",
|
||||||
|
action="store_true",
|
||||||
|
help="Skip OpenCollective backers if the members endpoint is unavailable.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--opencollective-data",
|
||||||
|
metavar="FILE",
|
||||||
|
help="Take OpenCollective API reply from FILE.",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
try:
|
||||||
|
since = compute_since_date(args.since)
|
||||||
|
oc_sponsors = []
|
||||||
|
if not args.skip_opencollective:
|
||||||
|
manual_data = (
|
||||||
|
pathlib.Path(args.opencollective_data).read_text()) \
|
||||||
|
if args.opencollective_data \
|
||||||
|
else None
|
||||||
|
oc_sponsors = opencollective_sponsors(since, manual_data)
|
||||||
|
section = render(since, github_sponsors(since), oc_sponsors)
|
||||||
|
except SponsorDataError as exc:
|
||||||
|
print(f"error: {exc}", file=sys.stderr)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if args.output:
|
||||||
|
with open(args.output, "w", encoding="utf-8") as f:
|
||||||
|
f.write(section)
|
||||||
|
else:
|
||||||
|
print(section, end="")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
4
.github/workflows/add_version.yml
vendored
4
.github/workflows/add_version.yml
vendored
|
|
@ -14,8 +14,8 @@ jobs:
|
||||||
add_cpython:
|
add_cpython:
|
||||||
runs-on: ubuntu-slim
|
runs-on: ubuntu-slim
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- uses: actions/setup-python@v6
|
- uses: actions/setup-python@v7
|
||||||
with:
|
with:
|
||||||
python-version: 3
|
python-version: 3
|
||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
|
|
|
||||||
62
.github/workflows/build.yml
vendored
Normal file
62
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
name: build
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
discover_build_logic_change:
|
||||||
|
if: github.event_name == 'pull_request'
|
||||||
|
runs-on: ubuntu-slim
|
||||||
|
env:
|
||||||
|
FULL_OS_MATRIX: '[
|
||||||
|
"macos-14",
|
||||||
|
"macos-15",
|
||||||
|
"macos-15-intel",
|
||||||
|
"macos-26",
|
||||||
|
"macos-26-intel",
|
||||||
|
"ubuntu-22.04",
|
||||||
|
"ubuntu-22.04-arm",
|
||||||
|
"ubuntu-24.04",
|
||||||
|
"ubuntu-24.04-arm"
|
||||||
|
]'
|
||||||
|
outputs:
|
||||||
|
os: ${{ steps.detect.outputs.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- run: git fetch origin "$GITHUB_BASE_REF"
|
||||||
|
- id: detect
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
if [[ -n $(git diff --name-only "origin/$GITHUB_BASE_REF" -- \
|
||||||
|
plugins/python-build/bin/python-build \
|
||||||
|
.github/actions/build-python/action.yml \
|
||||||
|
.github/workflows/build.yml) ]]
|
||||||
|
then
|
||||||
|
printf "%s\n" "os<<!" "$FULL_OS_MATRIX" "!" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
build:
|
||||||
|
needs: discover_build_logic_change
|
||||||
|
# Run when the detection job succeeded *or was skipped* -- but not failed
|
||||||
|
if: ${{ !cancelled() && !failure() }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
os: ${{ fromJson(needs.discover_build_logic_change.outputs.os || '["macos-latest","ubuntu-latest"]') }}
|
||||||
|
python-version:
|
||||||
|
- "3.10"
|
||||||
|
- "3.11"
|
||||||
|
- "3.12"
|
||||||
|
- "3.13"
|
||||||
|
- "3.14"
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- uses: ./.github/actions/build-python
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
|
||||||
40
.github/workflows/macos_build.yml
vendored
40
.github/workflows/macos_build.yml
vendored
|
|
@ -1,40 +0,0 @@
|
||||||
name: macos_build
|
|
||||||
on: [pull_request, push]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read # to fetch code (actions/checkout)
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
macos_build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
python-version:
|
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
|
||||||
- "3.12"
|
|
||||||
- "3.13"
|
|
||||||
- "3.14"
|
|
||||||
runs-on: macos-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- run: |
|
|
||||||
brew install openssl readline sqlite3 xz tcl-tk@8 libb2 zstd
|
|
||||||
- run: |
|
|
||||||
#envvars
|
|
||||||
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
|
||||||
echo "PYENV_ROOT=$PYENV_ROOT" >> $GITHUB_ENV
|
|
||||||
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
|
||||||
- run: |
|
|
||||||
#build
|
|
||||||
pyenv --debug install ${{ matrix.python-version }} && rc=$? || rc=$?
|
|
||||||
if [[ $rc -ne 0 ]]; then echo config.log:; cat $TMPDIR/python-build*/*/config.log; false; fi
|
|
||||||
pyenv global ${{ matrix.python-version }}
|
|
||||||
- run: |
|
|
||||||
#print version
|
|
||||||
python --version
|
|
||||||
python -m pip --version
|
|
||||||
- shell: python # Prove that actual Python == expected Python
|
|
||||||
env:
|
|
||||||
EXPECTED_PYTHON: ${{ matrix.python-version }}
|
|
||||||
run: import os, sys ; assert sys.version.startswith(os.getenv("EXPECTED_PYTHON"))
|
|
||||||
73
.github/workflows/modified_scripts_build.yml
vendored
73
.github/workflows/modified_scripts_build.yml
vendored
|
|
@ -2,13 +2,13 @@ name: modified_scripts
|
||||||
on: [pull_request]
|
on: [pull_request]
|
||||||
jobs:
|
jobs:
|
||||||
discover_modified_scripts:
|
discover_modified_scripts:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-slim
|
||||||
outputs:
|
outputs:
|
||||||
versions: ${{steps.modified-versions.outputs.versions}}
|
versions: ${{steps.modified-versions.outputs.versions}}
|
||||||
versions_cpython_only: ${{steps.modified-versions.outputs.versions_cpython_only}}
|
versions_cpython_only: ${{steps.modified-versions.outputs.versions_cpython_only}}
|
||||||
versions_macos_build_exclude: ${{steps.modified-versions.outputs.versions_macos_build_exclude}}
|
versions_macos_build_exclude: ${{steps.modified-versions.outputs.versions_macos_build_exclude}}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- run: git fetch origin "$GITHUB_BASE_REF"
|
- run: git fetch origin "$GITHUB_BASE_REF"
|
||||||
- shell: bash
|
- shell: bash
|
||||||
run: >
|
run: >
|
||||||
|
|
@ -43,28 +43,32 @@ jobs:
|
||||||
import packaging.version
|
import packaging.version
|
||||||
|
|
||||||
result=[]
|
result=[]
|
||||||
|
def exclude_macos_intel(result, python_version):
|
||||||
|
result.append({'os':'macos-15-intel','python-version':python_version})
|
||||||
|
result.append({'os':'macos-26-intel','python-version':python_version})
|
||||||
|
|
||||||
|
|
||||||
for line in os.environ['versions'].splitlines():
|
for line in os.environ['versions'].splitlines():
|
||||||
if m:=re.match(r'([^-]+)-(\d+\.\d+)-(\d+\.\d+.\d+)', line):
|
if m:=re.match(r'miniconda3-\d+\.\d+-(\d+\.\d+\.\d+)', line):
|
||||||
name, version = m.group(1), packaging.version.Version(m.group(3))
|
version = packaging.version.Version(m.group(1))
|
||||||
|
|
||||||
# Miniconda dropped MacOS x64 support
|
# Miniconda dropped MacOS x64 support
|
||||||
if (name == 'miniconda3' and version >= packaging.version.Version('25.9.1')):
|
if version >= packaging.version.Version('25.9.1'):
|
||||||
result.append({'os':'macos-15-intel','python-version':line})
|
exclude_macos_intel(result, line)
|
||||||
|
|
||||||
if m:=re.match(r'([^-]+)-(\d+\.\d+)', line):
|
if m:=re.match(r'anaconda3-(\d+\.\d+)', line):
|
||||||
name, version = m.group(1), packaging.version.Version(m.group(2))
|
version = packaging.version.Version(m.group(1))
|
||||||
|
|
||||||
# Anaconda dropped MacOS x64 support
|
# Anaconda dropped MacOS x64 support
|
||||||
if name == 'anaconda3' and version >= packaging.version.Version('2025.12'):
|
if version >= packaging.version.Version('2025.12'):
|
||||||
result.append({'os':'macos-15-intel','python-version':line})
|
exclude_macos_intel(result, line)
|
||||||
|
|
||||||
if m:=re.match(r'graalpy-(community-)?(\d+\.\d+.\d+)', line):
|
if m:=re.match(r'graalpy[^-]*-(community-)?(\d+\.\d+\.\d+)', line):
|
||||||
version = packaging.version.Version(m.group(2))
|
version = packaging.version.Version(m.group(2))
|
||||||
|
|
||||||
# GraalPy dropped MacOS x64 support
|
# GraalPy dropped MacOS x64 support
|
||||||
if version >= packaging.version.Version('25.0.2'):
|
if version >= packaging.version.Version('25.0.2'):
|
||||||
result.append({'os':'macos-15-intel','python-version':line})
|
exclude_macos_intel(result, line)
|
||||||
|
|
||||||
|
|
||||||
EOF = str(random.getrandbits(15*8))
|
EOF = str(random.getrandbits(15*8))
|
||||||
|
|
@ -91,10 +95,11 @@ jobs:
|
||||||
- macos-15
|
- macos-15
|
||||||
- macos-15-intel
|
- macos-15-intel
|
||||||
- macos-26
|
- macos-26
|
||||||
|
- macos-26-intel
|
||||||
exclude: ${{fromJson(needs.discover_modified_scripts.outputs.versions_macos_build_exclude)}}
|
exclude: ${{fromJson(needs.discover_modified_scripts.outputs.versions_macos_build_exclude)}}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- run: |
|
- run: |
|
||||||
#envvars
|
#envvars
|
||||||
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
||||||
|
|
@ -153,10 +158,15 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ${{fromJson(needs.discover_modified_scripts.outputs.versions_cpython_only)}}
|
python-version: ${{fromJson(needs.discover_modified_scripts.outputs.versions_cpython_only)}}
|
||||||
os: ["macos-14", "macos-15", "macos-15-intel"]
|
os:
|
||||||
|
- macos-14
|
||||||
|
- macos-15
|
||||||
|
- macos-15-intel
|
||||||
|
- macos-26
|
||||||
|
- macos-26-intel
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- run: |
|
- run: |
|
||||||
#envvars
|
#envvars
|
||||||
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
||||||
|
|
@ -165,12 +175,7 @@ jobs:
|
||||||
- run: |
|
- run: |
|
||||||
#prerequisites
|
#prerequisites
|
||||||
brew install sqlite3 xz tcl-tk@8 libb2 zstd
|
brew install sqlite3 xz tcl-tk@8 libb2 zstd
|
||||||
"$GITHUB_WORKSPACE/.github/workflows/scripts/brew-uninstall-cascade.sh" openssl@3 openssl@1.1 readline
|
"$GITHUB_WORKSPACE/.github/workflows/scripts/brew-uninstall-cascade.sh" $(brew list | grep -E '^openssl(@|$)') readline
|
||||||
if [[ "${{ matrix.python-version }}" =~ pypy.*-(src|dev) ]]; then
|
|
||||||
export PYENV_BOOTSTRAP_VERSION=pypy2.7-7
|
|
||||||
echo "PYENV_BOOTSTRAP_VERSION=$PYENV_BOOTSTRAP_VERSION" >> $GITHUB_ENV
|
|
||||||
pyenv install $PYENV_BOOTSTRAP_VERSION
|
|
||||||
fi
|
|
||||||
- run: |
|
- run: |
|
||||||
#build
|
#build
|
||||||
pyenv --debug install ${{ matrix.python-version }} && rc=$? || rc=$?
|
pyenv --debug install ${{ matrix.python-version }} && rc=$? || rc=$?
|
||||||
|
|
@ -202,10 +207,12 @@ jobs:
|
||||||
python-version: ${{fromJson(needs.discover_modified_scripts.outputs.versions)}}
|
python-version: ${{fromJson(needs.discover_modified_scripts.outputs.versions)}}
|
||||||
os:
|
os:
|
||||||
- ubuntu-22.04
|
- ubuntu-22.04
|
||||||
|
- ubuntu-22.04-arm
|
||||||
- ubuntu-24.04
|
- ubuntu-24.04
|
||||||
|
- ubuntu-24.04-arm
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- run: |
|
- run: |
|
||||||
#envvars
|
#envvars
|
||||||
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
||||||
|
|
@ -213,10 +220,7 @@ jobs:
|
||||||
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
||||||
- run: |
|
- run: |
|
||||||
#prerequisites
|
#prerequisites
|
||||||
sudo apt-get update -q; sudo apt-get install -yq make build-essential \
|
pyenv install-prerequisites
|
||||||
libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev \
|
|
||||||
curl llvm libncurses5-dev libncursesw5-dev \
|
|
||||||
xz-utils tk-dev libffi-dev liblzma-dev
|
|
||||||
if [[ "${{ matrix.python-version }}" =~ pypy.*-(src|dev) ]]; then
|
if [[ "${{ matrix.python-version }}" =~ pypy.*-(src|dev) ]]; then
|
||||||
export PYENV_BOOTSTRAP_VERSION=pypy2.7-7
|
export PYENV_BOOTSTRAP_VERSION=pypy2.7-7
|
||||||
echo "PYENV_BOOTSTRAP_VERSION=$PYENV_BOOTSTRAP_VERSION" >> $GITHUB_ENV
|
echo "PYENV_BOOTSTRAP_VERSION=$PYENV_BOOTSTRAP_VERSION" >> $GITHUB_ENV
|
||||||
|
|
@ -265,27 +269,18 @@ jobs:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
python-version: ${{fromJson(needs.discover_modified_scripts.outputs.versions_cpython_only)}}
|
python-version: ${{fromJson(needs.discover_modified_scripts.outputs.versions_cpython_only)}}
|
||||||
os: ["ubuntu-latest"]
|
runs-on: ubuntu-latest
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- run: |
|
- run: |
|
||||||
#envvars
|
#envvars
|
||||||
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
export PYENV_ROOT="$GITHUB_WORKSPACE"
|
||||||
echo "PYENV_ROOT=$PYENV_ROOT" >> $GITHUB_ENV
|
echo "PYENV_ROOT=$PYENV_ROOT" >> $GITHUB_ENV
|
||||||
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
||||||
echo "_PYTHON_BUILD_FORCE_SKIP_XZ=1" >> $GITHUB_PATH
|
echo "_PYTHON_BUILD_FORCE_SKIP_XZ=1" >> $GITHUB_ENV
|
||||||
- run: |
|
- run: |
|
||||||
#prerequisites
|
#prerequisites
|
||||||
sudo apt-get update -q; sudo apt-get install -yq make build-essential \
|
pyenv install-prerequisites
|
||||||
libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev \
|
|
||||||
curl llvm libncurses5-dev libncursesw5-dev \
|
|
||||||
xz-utils tk-dev libffi-dev liblzma-dev
|
|
||||||
if [[ "${{ matrix.python-version }}" =~ pypy.*-(src|dev) ]]; then
|
|
||||||
export PYENV_BOOTSTRAP_VERSION=pypy2.7-7
|
|
||||||
echo "PYENV_BOOTSTRAP_VERSION=$PYENV_BOOTSTRAP_VERSION" >> $GITHUB_ENV
|
|
||||||
pyenv install $PYENV_BOOTSTRAP_VERSION
|
|
||||||
fi
|
|
||||||
- run: |
|
- run: |
|
||||||
#build
|
#build
|
||||||
pyenv --debug install ${{ matrix.python-version }} && rc=$? || rc=$?
|
pyenv --debug install ${{ matrix.python-version }} && rc=$? || rc=$?
|
||||||
|
|
|
||||||
30
.github/workflows/pyenv_tests.yml
vendored
30
.github/workflows/pyenv_tests.yml
vendored
|
|
@ -1,5 +1,8 @@
|
||||||
name: pyenv_tests
|
name: pyenv_tests
|
||||||
on: [pull_request, push]
|
on:
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
pull_request: {}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read # to fetch code (actions/checkout)
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
@ -11,33 +14,32 @@ jobs:
|
||||||
matrix:
|
matrix:
|
||||||
os:
|
os:
|
||||||
- ubuntu-22.04
|
- ubuntu-22.04
|
||||||
|
- ubuntu-22.04-arm
|
||||||
- ubuntu-24.04
|
- ubuntu-24.04
|
||||||
|
- ubuntu-24.04-arm
|
||||||
- macos-14
|
- macos-14
|
||||||
- macos-15
|
- macos-15
|
||||||
- macos-15-intel
|
- macos-15-intel
|
||||||
- macos-26
|
- macos-26
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v7
|
||||||
- run: |
|
- name: Install prerequisites
|
||||||
|
run: |
|
||||||
if test "$RUNNER_OS" == "macOS"; then
|
if test "$RUNNER_OS" == "macOS"; then
|
||||||
brew install coreutils fish
|
brew install coreutils fish
|
||||||
fi
|
fi
|
||||||
- run: pwd
|
- name: Run tests
|
||||||
- env:
|
|
||||||
PYENV_ROOT: /home/runner/work/pyenv/pyenv
|
|
||||||
run: |
|
|
||||||
echo $PYENV_ROOT
|
|
||||||
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
|
||||||
- name: Run test on the host
|
|
||||||
run: |
|
run: |
|
||||||
make test
|
make test
|
||||||
- name: Run test with docker
|
|
||||||
if: ${{ ! contains(matrix.os, 'macos') }}
|
|
||||||
run: |
|
|
||||||
make test-docker
|
|
||||||
- env:
|
- env:
|
||||||
PYENV_NATIVE_EXT: 1
|
PYENV_NATIVE_EXT: 1
|
||||||
run: |
|
run: |
|
||||||
(cd src; ./configure; make)
|
(cd src; ./configure; make)
|
||||||
bats/bin/bats test/{pyenv,hooks,versions}.bats
|
bats/bin/bats test/{pyenv,hooks,versions}.bats
|
||||||
|
pyenv_tests_docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v7
|
||||||
|
- run: |
|
||||||
|
make test-docker
|
||||||
|
|
|
||||||
40
.github/workflows/ubuntu_build.yml
vendored
40
.github/workflows/ubuntu_build.yml
vendored
|
|
@ -1,40 +0,0 @@
|
||||||
name: ubuntu_build
|
|
||||||
on: [pull_request, push]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read # to fetch code (actions/checkout)
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ubuntu_build:
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
python-version:
|
|
||||||
- "3.10"
|
|
||||||
- "3.11"
|
|
||||||
- "3.12"
|
|
||||||
- "3.13"
|
|
||||||
- "3.14"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
- run: |
|
|
||||||
sudo apt-get update -q; sudo apt install -yq make build-essential libssl-dev zlib1g-dev \
|
|
||||||
libbz2-dev libreadline-dev libsqlite3-dev curl \
|
|
||||||
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
|
|
||||||
# https://github.com/pyenv/pyenv#installation
|
|
||||||
- run: pwd
|
|
||||||
- env:
|
|
||||||
PYENV_ROOT: /home/runner/work/pyenv/pyenv
|
|
||||||
run: |
|
|
||||||
echo $PYENV_ROOT
|
|
||||||
echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH
|
|
||||||
bin/pyenv --debug install ${{ matrix.python-version }}
|
|
||||||
bin/pyenv global ${{ matrix.python-version }}
|
|
||||||
bin/pyenv rehash
|
|
||||||
- run: python --version
|
|
||||||
- run: python -m pip --version
|
|
||||||
- shell: python # Prove that actual Python == expected Python
|
|
||||||
env:
|
|
||||||
EXPECTED_PYTHON: ${{ matrix.python-version }}
|
|
||||||
run: import os, sys ; assert sys.version.startswith(os.getenv("EXPECTED_PYTHON"))
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,3 +11,4 @@
|
||||||
/default-packages
|
/default-packages
|
||||||
.idea
|
.idea
|
||||||
*.un~
|
*.un~
|
||||||
|
*.swp
|
||||||
|
|
|
||||||
62
CHANGELOG.md
62
CHANGELOG.md
|
|
@ -1,5 +1,63 @@
|
||||||
# Version History
|
# Version History
|
||||||
|
|
||||||
|
## Release v2.8.5
|
||||||
|
* pyenv-binary: record direct system dependencies in Linux/FreeBSD by @macayu17 in https://github.com/pyenv/pyenv/pull/3520
|
||||||
|
* tests: Fix "cd: null directory" in OpenSUSE by @native-api in https://github.com/pyenv/pyenv/pull/3524
|
||||||
|
* Test improvements by @native-api in https://github.com/pyenv/pyenv/pull/3525
|
||||||
|
* CI: test on all runners when the build logic changes by @sujeito-operator in https://github.com/pyenv/pyenv/pull/3522
|
||||||
|
* Add GraalPy 25.3.4.1 by @msimacek in https://github.com/pyenv/pyenv/pull/3529
|
||||||
|
* Add CPython 3.15.0rc2 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3530
|
||||||
|
* pyenv-binary: support relocating macOS archives by @macayu17 in https://github.com/pyenv/pyenv/pull/3528
|
||||||
|
|
||||||
|
## Release v2.8.4
|
||||||
|
* CI: Add macos_26_intel by @native-api in https://github.com/pyenv/pyenv/pull/3514
|
||||||
|
* pyenv-binary: check FreeBSD system libraries by @macayu17 in https://github.com/pyenv/pyenv/pull/3511
|
||||||
|
* version-file: Fix infinite loop for a relative path argument by @fudianchn in https://github.com/pyenv/pyenv/pull/3515
|
||||||
|
* pyenv-binary: check patchelf early and add --verbose by @macayu17 in https://github.com/pyenv/pyenv/pull/3516
|
||||||
|
* CI: add ubuntu ARM images by @native-api in https://github.com/pyenv/pyenv/pull/3517
|
||||||
|
* Add CPython 3.10.21, 3.11.16, 3.12.14 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3518
|
||||||
|
* Add anaconda3-2026.07-1, miniconda3 26.5.3-2 by @native-api in https://github.com/pyenv/pyenv/pull/3519
|
||||||
|
|
||||||
|
## Release v2.8.3
|
||||||
|
* Add CPython 3.13.15, 3.14.7 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3512
|
||||||
|
|
||||||
|
## Release v2.8.2
|
||||||
|
* pyenv-binary: add the `package` subcommand; add `pyenv install --list --bare` by @macayu17 in https://github.com/pyenv/pyenv/pull/3498
|
||||||
|
* Add graalpy3.12-25.2.4 by @msimacek in https://github.com/pyenv/pyenv/pull/3502
|
||||||
|
* Bump actions/setup-python from 6 to 7 in the github-actions group by @dependabot[bot] in https://github.com/pyenv/pyenv/pull/3505
|
||||||
|
* add_cpython: refactor extra Requests logic by @anupamme in https://github.com/pyenv/pyenv/pull/3507
|
||||||
|
* Add CPython 3.15.0rc1 by @jsirois in https://github.com/pyenv/pyenv/pull/3510
|
||||||
|
* pyenv-binary: generate platform-specific package names by @macayu17 in https://github.com/pyenv/pyenv/pull/3504
|
||||||
|
|
||||||
|
## Release v2.8.1
|
||||||
|
* pyenv-binary: add the `generate-installer` subcommand by @macayu17 in https://github.com/pyenv/pyenv/pull/3488
|
||||||
|
* Add CPython 3.15.0b4 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3495
|
||||||
|
|
||||||
|
## Release v2.8.0
|
||||||
|
* CVE-2026-68939: Fix handling of glob characters in .python-version
|
||||||
|
* CI: add_version enhancements by @native-api in https://github.com/pyenv/pyenv/pull/3480
|
||||||
|
* Add miniforge3-26.3.2-2, 26.3.2-3 by @native-api in https://github.com/pyenv/pyenv/pull/3481
|
||||||
|
* rehash: fix race condition in landlock writability check by @Sheile in https://github.com/pyenv/pyenv/pull/3483
|
||||||
|
* Add script to generate sponsors section for release notes by @macayu17 in https://github.com/pyenv/pyenv/pull/3478
|
||||||
|
* install: Add an ability to install a version under an alias by @macayu17 in https://github.com/pyenv/pyenv/pull/3484
|
||||||
|
* Add graalpy-3.12-25.1.3 by @msimacek in https://github.com/pyenv/pyenv/pull/3485
|
||||||
|
* Bump actions/checkout from 6 to 7 in the github-actions group by @dependabot[bot] in https://github.com/pyenv/pyenv/pull/3486
|
||||||
|
* Add an experimental pyenv-binary plugin with a save command by @macayu17 in https://github.com/pyenv/pyenv/pull/3487
|
||||||
|
* CI: dependabot: set cooldown by @orbisai0security in https://github.com/pyenv/pyenv/pull/3489
|
||||||
|
* Fix typos in docs, scripts, and tests by @maxtaran2010 in https://github.com/pyenv/pyenv/pull/3490
|
||||||
|
* Add miniconda3 26.5.3-1 by @native-api in https://github.com/pyenv/pyenv/pull/3491
|
||||||
|
* version-name: skip redundant checks by @native-api in https://github.com/pyenv/pyenv/pull/3492
|
||||||
|
|
||||||
|
## Release v2.7.3
|
||||||
|
* CI: add_version enhancements by @macayu17 in https://github.com/pyenv/pyenv/pull/3475
|
||||||
|
* Add CPython 3.15.0b3 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3479
|
||||||
|
|
||||||
|
## Release v2.7.2
|
||||||
|
* fix(rehash): prevent terminal hang caused by stale or sandbox-blocked lock file by @anupddas in https://github.com/pyenv/pyenv/pull/3469
|
||||||
|
* 3.6.x: Fix verify_* calls by @native-api in https://github.com/pyenv/pyenv/commit/6c0c5cfa9619e4a7a90102d8bee6c771c4739836
|
||||||
|
* Add CPython 3.14.6 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3472
|
||||||
|
* Add CPython 3.13.14 by @pyenv-bot[bot] in https://github.com/pyenv/pyenv/pull/3473
|
||||||
|
|
||||||
## Release v2.7.1
|
## Release v2.7.1
|
||||||
* Support 3.9 EOL Pip URL, consolidate tests by @native-api in https://github.com/pyenv/pyenv/pull/3465
|
* Support 3.9 EOL Pip URL, consolidate tests by @native-api in https://github.com/pyenv/pyenv/pull/3465
|
||||||
* Update URLs for PyPy nightly; Remove pypy3.5 and pypy3.7 nightly by @native-api in https://github.com/pyenv/pyenv/pull/3466
|
* Update URLs for PyPy nightly; Remove pypy3.5 and pypy3.7 nightly by @native-api in https://github.com/pyenv/pyenv/pull/3466
|
||||||
|
|
@ -359,7 +417,7 @@
|
||||||
* Add CPython 3.13.0a4 by @saaketp in https://github.com/pyenv/pyenv/pull/2903
|
* Add CPython 3.13.0a4 by @saaketp in https://github.com/pyenv/pyenv/pull/2903
|
||||||
* Handle the case where `pyenv-commands --sh` returns nothing by @aphedges in https://github.com/pyenv/pyenv/pull/2908
|
* Handle the case where `pyenv-commands --sh` returns nothing by @aphedges in https://github.com/pyenv/pyenv/pull/2908
|
||||||
* Document default build configuration customizations by @native-api in https://github.com/pyenv/pyenv/pull/2911
|
* Document default build configuration customizations by @native-api in https://github.com/pyenv/pyenv/pull/2911
|
||||||
* Use Homebrew in Linux if Pyenv is installled with Homebrew by @native-api in https://github.com/pyenv/pyenv/pull/2906
|
* Use Homebrew in Linux if Pyenv is installed with Homebrew by @native-api in https://github.com/pyenv/pyenv/pull/2906
|
||||||
* Add miniforge and mambaforge 22.11.1-3, 22.11.1-4, 23.1.0-0 to 23.11.0-0 by @aphedges in https://github.com/pyenv/pyenv/pull/2909
|
* Add miniforge and mambaforge 22.11.1-3, 22.11.1-4, 23.1.0-0 to 23.11.0-0 by @aphedges in https://github.com/pyenv/pyenv/pull/2909
|
||||||
* Add miniconda3-24.1.2 by @binbjz in https://github.com/pyenv/pyenv/pull/2915
|
* Add miniconda3-24.1.2 by @binbjz in https://github.com/pyenv/pyenv/pull/2915
|
||||||
* Minor grammar fix in libffi backport patch in 2.5.x by @cuinix in https://github.com/pyenv/pyenv/pull/2922
|
* Minor grammar fix in libffi backport patch in 2.5.x by @cuinix in https://github.com/pyenv/pyenv/pull/2922
|
||||||
|
|
@ -1495,7 +1553,7 @@
|
||||||
* pyenv: Prefer gawk over awk if both are available.
|
* pyenv: Prefer gawk over awk if both are available.
|
||||||
* python-build: Add new PyPy release; pypy-2.3, pypy-2.3-src (#162)
|
* python-build: Add new PyPy release; pypy-2.3, pypy-2.3-src (#162)
|
||||||
* python-build: Add new Anaconda release; anaconda-1.9.2 (#155)
|
* python-build: Add new Anaconda release; anaconda-1.9.2 (#155)
|
||||||
* python-build: Add new Miniconda releases; miniconda-3.3.0, minoconda-3.4.2, miniconda3-3.3.0, miniconda3-3.4.2
|
* python-build: Add new Miniconda releases; miniconda-3.3.0, miniconda-3.4.2, miniconda3-3.3.0, miniconda3-3.4.2
|
||||||
* python-build: Add new Stackless releases; stackless-2.7.3, stackless-2.7.4, stackless-2.7.5, stackless-2.7.6, stackless-3.2.5, stackless-3.3.5 (#164)
|
* python-build: Add new Stackless releases; stackless-2.7.3, stackless-2.7.4, stackless-2.7.5, stackless-2.7.6, stackless-3.2.5, stackless-3.3.5 (#164)
|
||||||
* python-build: Add IronPython versions (setuptools and pip will work); ironpython-2.7.4, ironpython-dev
|
* python-build: Add IronPython versions (setuptools and pip will work); ironpython-2.7.4, ironpython-dev
|
||||||
* python-build: Add new Jython beta release; jython-2.7-beta2
|
* python-build: Add new Jython beta release; jython-2.7-beta2
|
||||||
|
|
|
||||||
|
|
@ -202,8 +202,8 @@ or, if you prefer 3.3.3 over 2.7.6,
|
||||||
|
|
||||||
Install a Python version (using [`python-build`](https://github.com/pyenv/pyenv/tree/master/plugins/python-build)).
|
Install a Python version (using [`python-build`](https://github.com/pyenv/pyenv/tree/master/plugins/python-build)).
|
||||||
|
|
||||||
Usage: pyenv install [-f] [-kvp] <version>
|
Usage: pyenv install [-f] [-kvp] <version>[:<alias>]
|
||||||
pyenv install [-f] [-kvp] <definition-file>
|
pyenv install [-f] [-kvp] <definition-file>[:<alias>]
|
||||||
pyenv install -l|--list
|
pyenv install -l|--list
|
||||||
|
|
||||||
-l/--list List all available versions
|
-l/--list List all available versions
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,14 @@ Release checklist:
|
||||||
* Start [drafting a new release on GitHub](https://github.com/pyenv/pyenv/releases) to generate a summary of changes.
|
* Start [drafting a new release on GitHub](https://github.com/pyenv/pyenv/releases) to generate a summary of changes.
|
||||||
Type the would-be tag name in the "Choose a tag" field and press "Generate release notes"
|
Type the would-be tag name in the "Choose a tag" field and press "Generate release notes"
|
||||||
* The summary may need editing. E.g. rephrase entries, delete/merge entries that are too minor or irrelevant to the users (e.g. typo fixes, CI)
|
* The summary may need editing. E.g. rephrase entries, delete/merge entries that are too minor or irrelevant to the users (e.g. typo fixes, CI)
|
||||||
|
* Add a sponsors section to the release notes by running:
|
||||||
|
```bash
|
||||||
|
.github/scripts/generate_release_notes_sponsors.py
|
||||||
|
```
|
||||||
|
Paste the output at the end of the release notes.
|
||||||
|
* This lists new GitHub Sponsors and OpenCollective backers since the last release or within the last month, whichever is longer.
|
||||||
|
* The GitHub Sponsors query requires the `gh` CLI with the `read:user` scope.
|
||||||
|
* If OpenCollective is unavailable, pass `--skip-opencollective` and add those backers manually.
|
||||||
* Update `CHANGELOG.md` with the new version number and the edited summary (only the changes section)
|
* Update `CHANGELOG.md` with the new version number and the edited summary (only the changes section)
|
||||||
* Push the version number in `libexec/pyenv---version` and `plugins/python-build/bin/python-build`
|
* Push the version number in `libexec/pyenv---version` and `plugins/python-build/bin/python-build`
|
||||||
* Minor version is pushed if there are significant functional changes (not e.g. bugfixes/formula adaptations/supporting niche use cases).
|
* Minor version is pushed if there are significant functional changes (not e.g. bugfixes/formula adaptations/supporting niche use cases).
|
||||||
|
|
|
||||||
131
Makefile
131
Makefile
|
|
@ -1,16 +1,24 @@
|
||||||
TEST_BATS_VERSION = v1.10.0
|
TEST_BATS_VERSION = v1.10.0
|
||||||
TEST_BASH_VERSIONS = 3.2.57 4.1.17
|
TEST_BASH_VERSIONS = 3.2.57 4.1.17
|
||||||
|
|
||||||
TEST_UNIT_DOCKER_PREFIX = test-unit-docker
|
TEST_UNIT_DOCKER_PREFIX = test-unit-docker
|
||||||
TEST_UNIT_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_UNIT_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_UNIT_DOCKER_PREFIX)))
|
TEST_UNIT_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_UNIT_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_UNIT_DOCKER_PREFIX)))
|
||||||
TEST_PLUGIN_DOCKER_PREFIX = test-plugin-docker
|
|
||||||
TEST_PLUGIN_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_PLUGIN_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_PLUGIN_DOCKER_PREFIX)))
|
TEST_PYTHON_BUILD_DOCKER_PREFIX = test-python-build-docker
|
||||||
|
TEST_PYTHON_BUILD_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_PYTHON_BUILD_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_PYTHON_BUILD_DOCKER_PREFIX)))
|
||||||
|
|
||||||
|
TEST_BINARY_DOCKER_PREFIX = test-binary-docker
|
||||||
|
TEST_BINARY_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_BINARY_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_BINARY_DOCKER_PREFIX)))
|
||||||
|
|
||||||
|
TEST_LINK_DOCKER_PREFIX = test-link-docker
|
||||||
|
TEST_LINK_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_LINK_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_LINK_DOCKER_PREFIX)))
|
||||||
|
|
||||||
TEST_BATS_IMAGE_PREFIX = test-pyenv-docker-image
|
TEST_BATS_IMAGE_PREFIX = test-pyenv-docker-image
|
||||||
TEST_BATS_IMAGE_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_BATS_IMAGE_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_BATS_IMAGE_PREFIX)))
|
TEST_BATS_IMAGE_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_BATS_IMAGE_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_BATS_IMAGE_PREFIX)))
|
||||||
|
|
||||||
.PHONY:
|
.PHONY: test-docker
|
||||||
test-docker: $(TEST_UNIT_DOCKER_PREFIX) $(TEST_PLUGIN_DOCKER_PREFIX)
|
test-docker: $(TEST_UNIT_DOCKER_PREFIX) $(TEST_PYTHON_BUILD_DOCKER_PREFIX) $(TEST_BINARY_DOCKER_PREFIX) $(TEST_LINK_DOCKER_PREFIX)
|
||||||
|
|
||||||
# Run all unit test under bats docker
|
|
||||||
.PHONY: $(TEST_UNIT_DOCKER_PREFIX)
|
.PHONY: $(TEST_UNIT_DOCKER_PREFIX)
|
||||||
$(TEST_UNIT_DOCKER_PREFIX): $(TEST_UNIT_DOCKER_TARGETS)
|
$(TEST_UNIT_DOCKER_PREFIX): $(TEST_UNIT_DOCKER_TARGETS)
|
||||||
|
|
||||||
|
|
@ -31,23 +39,22 @@ $(TEST_UNIT_DOCKER_TARGETS): $(TEST_UNIT_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PR
|
||||||
-u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \
|
-u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \
|
||||||
$${BATS_TEST_FILTER:+-e BATS_TEST_FILTER="$${BATS_TEST_FILTER}"} \
|
$${BATS_TEST_FILTER:+-e BATS_TEST_FILTER="$${BATS_TEST_FILTER}"} \
|
||||||
$${BATS_FILE_FILTER:+-e BATS_FILE_FILTER="$${BATS_FILE_FILTER}"} \
|
$${BATS_FILE_FILTER:+-e BATS_FILE_FILTER="$${BATS_FILE_FILTER}"} \
|
||||||
$${CI+-e CI="$${CI}"} \
|
$${CI+-e CI="$${CI}"} \
|
||||||
$(INTERACTIVE) \
|
$(INTERACTIVE) \
|
||||||
$(DOCKER_IMAGE):$(DOCKER_TAG) \
|
$(DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
test/run
|
test/run
|
||||||
|
|
||||||
# Run all plugin test under bats docker
|
.PHONY: $(TEST_PYTHON_BUILD_DOCKER_PREFIX)
|
||||||
.PHONY: $(TEST_PLUGIN_DOCKER_PREFIX)
|
$(TEST_PYTHON_BUILD_DOCKER_PREFIX): $(TEST_PYTHON_BUILD_DOCKER_TARGETS)
|
||||||
$(TEST_PLUGIN_DOCKER_PREFIX): $(TEST_PLUGIN_DOCKER_TARGETS)
|
|
||||||
|
|
||||||
# Run each plugin test under bats docker
|
# Run each plugin test under bats docker
|
||||||
.PHONY: $(TEST_PLUGIN_DOCKER_TARGETS)
|
.PHONY: $(TEST_PYTHON_BUILD_DOCKER_TARGETS)
|
||||||
$(TEST_PLUGIN_DOCKER_TARGETS): DOCKER_IMAGE = $(TEST_BATS_IMAGE_PREFIX)
|
$(TEST_PYTHON_BUILD_DOCKER_TARGETS): DOCKER_IMAGE = $(TEST_BATS_IMAGE_PREFIX)
|
||||||
$(TEST_PLUGIN_DOCKER_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False)
|
$(TEST_PYTHON_BUILD_DOCKER_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False)
|
||||||
$(TEST_PLUGIN_DOCKER_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@))
|
$(TEST_PYTHON_BUILD_DOCKER_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@))
|
||||||
$(TEST_PLUGIN_DOCKER_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU)
|
$(TEST_PYTHON_BUILD_DOCKER_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU)
|
||||||
$(TEST_PLUGIN_DOCKER_TARGETS): INTERACTIVE = $(if $(findstring true,$(CI)),,-ti)
|
$(TEST_PYTHON_BUILD_DOCKER_TARGETS): INTERACTIVE = $(if $(findstring true,$(CI)),,-ti)
|
||||||
$(TEST_PLUGIN_DOCKER_TARGETS): $(TEST_PLUGIN_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PREFIX)-%
|
$(TEST_PYTHON_BUILD_DOCKER_TARGETS): $(TEST_PYTHON_BUILD_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PREFIX)-%
|
||||||
$(info Running test with docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)')
|
$(info Running test with docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)')
|
||||||
docker run \
|
docker run \
|
||||||
--init \
|
--init \
|
||||||
|
|
@ -55,10 +62,57 @@ $(TEST_PLUGIN_DOCKER_TARGETS): $(TEST_PLUGIN_DOCKER_PREFIX)-% : $(TEST_BATS_IMAG
|
||||||
-v /etc/passwd:/etc/passwd:ro \
|
-v /etc/passwd:/etc/passwd:ro \
|
||||||
-v /etc/group:/etc/group:ro \
|
-v /etc/group:/etc/group:ro \
|
||||||
-u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \
|
-u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \
|
||||||
$${CI+-e CI="$${CI}"} \
|
$${CI+-e CI="$${CI}"} \
|
||||||
$(INTERACTIVE) \
|
$(INTERACTIVE) \
|
||||||
$(DOCKER_IMAGE):$(DOCKER_TAG) \
|
$(DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
bats $${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} plugins/python-build/test/$${BATS_FILE_FILTER}
|
bats $${CI:+-F "/code/test/libexec/bats-format-tap-suite"} \
|
||||||
|
$${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} plugins/python-build/test/$${BATS_FILE_FILTER}
|
||||||
|
|
||||||
|
.PHONY: $(TEST_BINARY_DOCKER_PREFIX)
|
||||||
|
$(TEST_BINARY_DOCKER_PREFIX): $(TEST_BINARY_DOCKER_TARGETS)
|
||||||
|
|
||||||
|
.PHONY: $(TEST_BINARY_DOCKER_TARGETS)
|
||||||
|
$(TEST_BINARY_DOCKER_TARGETS): DOCKER_IMAGE = $(TEST_BATS_IMAGE_PREFIX)
|
||||||
|
$(TEST_BINARY_DOCKER_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False)
|
||||||
|
$(TEST_BINARY_DOCKER_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@))
|
||||||
|
$(TEST_BINARY_DOCKER_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU)
|
||||||
|
$(TEST_BINARY_DOCKER_TARGETS): INTERACTIVE = $(if $(findstring true,$(CI)),,-ti)
|
||||||
|
$(TEST_BINARY_DOCKER_TARGETS): $(TEST_BINARY_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PREFIX)-%
|
||||||
|
$(info Running test with docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)')
|
||||||
|
docker run \
|
||||||
|
--init \
|
||||||
|
-v $(PWD):/code:ro \
|
||||||
|
-v /etc/passwd:/etc/passwd:ro \
|
||||||
|
-v /etc/group:/etc/group:ro \
|
||||||
|
-u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \
|
||||||
|
$${CI+-e CI="$${CI}"} \
|
||||||
|
$(INTERACTIVE) \
|
||||||
|
$(DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
|
bats $${CI:+-F "/code/test/libexec/bats-format-tap-suite"} \
|
||||||
|
$${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} plugins/pyenv-binary/test/$${BATS_FILE_FILTER}
|
||||||
|
|
||||||
|
.PHONY: $(TEST_LINK_DOCKER_PREFIX)
|
||||||
|
$(TEST_LINK_DOCKER_PREFIX): $(TEST_LINK_DOCKER_TARGETS)
|
||||||
|
|
||||||
|
.PHONY: $(TEST_LINK_DOCKER_TARGETS)
|
||||||
|
$(TEST_LINK_DOCKER_TARGETS): DOCKER_IMAGE = $(TEST_BATS_IMAGE_PREFIX)
|
||||||
|
$(TEST_LINK_DOCKER_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False)
|
||||||
|
$(TEST_LINK_DOCKER_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@))
|
||||||
|
$(TEST_LINK_DOCKER_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU)
|
||||||
|
$(TEST_LINK_DOCKER_TARGETS): INTERACTIVE = $(if $(findstring true,$(CI)),,-ti)
|
||||||
|
$(TEST_LINK_DOCKER_TARGETS): $(TEST_LINK_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PREFIX)-%
|
||||||
|
$(info Running test with docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)')
|
||||||
|
docker run \
|
||||||
|
--init \
|
||||||
|
-v $(PWD):/code:ro \
|
||||||
|
-v /etc/passwd:/etc/passwd:ro \
|
||||||
|
-v /etc/group:/etc/group:ro \
|
||||||
|
-u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \
|
||||||
|
$${CI+-e CI="$${CI}"} \
|
||||||
|
$(INTERACTIVE) \
|
||||||
|
$(DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
|
bats $${CI:+-F "/code/test/libexec/bats-format-tap-suite"} \
|
||||||
|
$${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} plugins/pyenv-link/test/$${BATS_FILE_FILTER}
|
||||||
|
|
||||||
# Build all images needed for bats under docker
|
# Build all images needed for bats under docker
|
||||||
.PHONY: $(TEST_BATS_IMAGE_PREFIX)
|
.PHONY: $(TEST_BATS_IMAGE_PREFIX)
|
||||||
|
|
@ -71,7 +125,7 @@ $(TEST_BATS_IMAGE_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False)
|
||||||
$(TEST_BATS_IMAGE_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@))
|
$(TEST_BATS_IMAGE_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@))
|
||||||
$(TEST_BATS_IMAGE_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU)
|
$(TEST_BATS_IMAGE_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU)
|
||||||
$(TEST_BATS_IMAGE_TARGETS):
|
$(TEST_BATS_IMAGE_TARGETS):
|
||||||
$(info Building docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)')
|
if [ -z "$$(docker images -q '$(DOCKER_IMAGE):$(DOCKER_TAG)')" ]]; then \
|
||||||
docker build \
|
docker build \
|
||||||
--quiet \
|
--quiet \
|
||||||
-f "$(PWD)/test/Dockerfile" \
|
-f "$(PWD)/test/Dockerfile" \
|
||||||
|
|
@ -79,41 +133,38 @@ $(TEST_BATS_IMAGE_TARGETS):
|
||||||
--build-arg BASH="$(BASH)" \
|
--build-arg BASH="$(BASH)" \
|
||||||
--build-arg BATS_VERSION="$(TEST_BATS_VERSION)" \
|
--build-arg BATS_VERSION="$(TEST_BATS_VERSION)" \
|
||||||
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
|
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
|
||||||
./
|
./ ; \
|
||||||
|
fi
|
||||||
|
|
||||||
.PHONY: test test-build test-unit test-plugin
|
.PHONY: test test-unit test-python-build test-binary test-link
|
||||||
|
|
||||||
# Do not pass in user flags to build tests.
|
# Do not pass in user flags to build tests.
|
||||||
unexport PYTHON_CFLAGS
|
unexport PYTHON_CFLAGS
|
||||||
unexport PYTHON_CONFIGURE_OPTS
|
unexport PYTHON_CONFIGURE_OPTS
|
||||||
|
|
||||||
test: test-unit test-plugin
|
test: test-unit test-python-build test-binary test-link
|
||||||
|
|
||||||
test-unit: bats
|
test-unit: bats
|
||||||
PATH="./bats/bin:$$PATH" test/run
|
PATH="./bats/bin:$$PATH" test/run
|
||||||
|
|
||||||
test-plugin: bats
|
test-python-build: bats
|
||||||
cd plugins/python-build && $(PWD)/bats/bin/bats $${CI:+--tap} $${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} test/$${BATS_FILE_FILTER}
|
cd plugins/python-build && $(PWD)/bats/bin/bats $${CI:+-F "$(PWD)/test/libexec/bats-format-tap-suite"} \
|
||||||
|
$${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} test/$${BATS_FILE_FILTER}
|
||||||
|
|
||||||
PYTHON_BUILD_ROOT := $(CURDIR)/plugins/python-build
|
test-binary: bats
|
||||||
PYTHON_BUILD_OPTS ?= --verbose
|
cd plugins/pyenv-binary && $(PWD)/bats/bin/bats $${CI:+-F "$(PWD)/test/libexec/bats-format-tap-suite"} \
|
||||||
PYTHON_BUILD_VERSION ?= 3.8-dev
|
$${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} test/$${BATS_FILE_FILTER}
|
||||||
PYTHON_BUILD_TEST_PREFIX ?= $(PYTHON_BUILD_ROOT)/test/build/tmp/dist
|
|
||||||
|
|
||||||
test-build:
|
test-link: bats
|
||||||
$(RM) -r $(PYTHON_BUILD_TEST_PREFIX)
|
cd plugins/pyenv-link && $(PWD)/bats/bin/bats $${CI:+-F "$(PWD)/test/libexec/bats-format-tap-suite"} \
|
||||||
$(PYTHON_BUILD_ROOT)/bin/python-build $(PYTHON_BUILD_OPTS) $(PYTHON_BUILD_VERSION) $(PYTHON_BUILD_TEST_PREFIX)
|
$${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} test/$${BATS_FILE_FILTER}
|
||||||
[ -e $(PYTHON_BUILD_TEST_PREFIX)/bin/python ]
|
|
||||||
$(PYTHON_BUILD_TEST_PREFIX)/bin/python -V
|
|
||||||
[ -e $(PYTHON_BUILD_TEST_PREFIX)/bin/pip ]
|
|
||||||
$(PYTHON_BUILD_TEST_PREFIX)/bin/pip -V
|
|
||||||
|
|
||||||
.SECONDARY: bats-$(TEST_BATS_VERSION)
|
.SECONDARY: bats-$(TEST_BATS_VERSION)
|
||||||
bats-$(TEST_BATS_VERSION):
|
bats-$(TEST_BATS_VERSION):
|
||||||
rm -rf bats
|
|
||||||
ln -sf bats-$(TEST_BATS_VERSION) bats
|
|
||||||
git clone --depth 1 --branch $(TEST_BATS_VERSION) https://github.com/bats-core/bats-core.git bats-$(TEST_BATS_VERSION)
|
git clone --depth 1 --branch $(TEST_BATS_VERSION) https://github.com/bats-core/bats-core.git bats-$(TEST_BATS_VERSION)
|
||||||
|
|
||||||
.PHONY: bats
|
.PHONY: bats
|
||||||
bats: bats-$(TEST_BATS_VERSION)
|
bats: bats-$(TEST_BATS_VERSION)
|
||||||
ln -sf bats-$(TEST_BATS_VERSION) bats
|
if [ \( ! -L bats \) -o \( "x$$(readlink bats)" != "xbats-$(TEST_BATS_VERSION)" \) ]; then \
|
||||||
|
rm -rf bats; ln -s bats-$(TEST_BATS_VERSION) bats; \
|
||||||
|
fi
|
||||||
|
|
|
||||||
266
README.md
266
README.md
|
|
@ -13,6 +13,8 @@ This project was forked from [rbenv](https://github.com/rbenv/rbenv) and
|
||||||
|
|
||||||
* Lets you **change the global Python version** on a per-user basis.
|
* Lets you **change the global Python version** on a per-user basis.
|
||||||
* Provides support for **per-project Python versions**.
|
* Provides support for **per-project Python versions**.
|
||||||
|
* Supports **multiple Python distributions**: CPython, PyPy, Stackless Python, Jython, and more.
|
||||||
|
See the [full list of available versions](https://github.com/pyenv/pyenv/tree/master/plugins/python-build/share/python-build).
|
||||||
* Allows you to **override the Python version** with an environment
|
* Allows you to **override the Python version** with an environment
|
||||||
variable.
|
variable.
|
||||||
* Searches for commands from **multiple versions of Python at a time**.
|
* Searches for commands from **multiple versions of Python at a time**.
|
||||||
|
|
@ -69,6 +71,7 @@ This project was forked from [rbenv](https://github.com/rbenv/rbenv) and
|
||||||
* [Using Pyenv without shims](#using-pyenv-without-shims)
|
* [Using Pyenv without shims](#using-pyenv-without-shims)
|
||||||
* [Running nested shells from Python-based programs](#running-nested-shells-from-python-based-programs)
|
* [Running nested shells from Python-based programs](#running-nested-shells-from-python-based-programs)
|
||||||
* [Environment variables](#environment-variables)
|
* [Environment variables](#environment-variables)
|
||||||
|
* [Manual shell setup](#manual-shell-setup)
|
||||||
* **[Development](#development)**
|
* **[Development](#development)**
|
||||||
* [Contributing](#contributing)
|
* [Contributing](#contributing)
|
||||||
* [Version History](#version-history)
|
* [Version History](#version-history)
|
||||||
|
|
@ -177,134 +180,29 @@ which does install native Windows Python versions.
|
||||||
----
|
----
|
||||||
|
|
||||||
The below setup should work for the vast majority of users for common use cases.
|
The below setup should work for the vast majority of users for common use cases.
|
||||||
See [Advanced configuration](#advanced-configuration) for details and more configuration options.
|
See [Advanced configuration](#advanced-configuration)
|
||||||
|
and specifically [Manual shell setup](#manual-shell-setup) for details and more configuration options.
|
||||||
|
|
||||||
If `pyenv` is already on `PATH`, you can configure the relevant shell startup
|
To add the suggested setup code to the startup files of the running shell,
|
||||||
files automatically:
|
run `<path/to/pyenv> --install`.
|
||||||
|
Specifically:
|
||||||
|
|
||||||
|
* If you installed Pyenv with the installer script:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
~/.pyenv/bin/pyenv init --install
|
||||||
|
```
|
||||||
|
|
||||||
|
* If you installed Pyenv with Homebrew:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
pyenv init --install
|
pyenv init --install
|
||||||
```
|
```
|
||||||
|
|
||||||
If `pyenv` is not on `PATH` yet, run the same command through the `pyenv`
|
|
||||||
executable in your chosen installation directory.
|
|
||||||
|
|
||||||
This uses the same shell detection as `pyenv init`. If a startup file already
|
|
||||||
contains Pyenv-related configuration, the command refuses to edit it; review the
|
|
||||||
file manually and run `pyenv init <shell>` to see the suggested setup.
|
|
||||||
|
|
||||||
For Bash, avoid the automatic `--install` path if your `BASH_ENV` points to
|
For Bash, avoid the automatic `--install` path if your `BASH_ENV` points to
|
||||||
`.bashrc`; use the manual Bash instructions below so the `eval "$(pyenv init - bash)"`
|
`.bashrc`; use the manual Bash instructions below so the `eval "$(pyenv init - bash)"`
|
||||||
line only goes in your login startup file.
|
line only goes in your login startup file.
|
||||||
|
|
||||||
#### Bash
|
|
||||||
<details>
|
|
||||||
|
|
||||||
Stock Bash startup files vary widely between distributions in which of them source
|
|
||||||
which, under what circumstances, in what order and what additional configuration they perform.
|
|
||||||
As such, the most reliable way to get Pyenv in all environments is to append Pyenv
|
|
||||||
configuration commands to both `.bashrc` (for interactive shells)
|
|
||||||
and the profile file that Bash would use (for login shells).
|
|
||||||
|
|
||||||
1. First, add the commands to `~/.bashrc` by running the following in your terminal:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
|
|
||||||
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
|
|
||||||
echo 'eval "$(pyenv init - bash)"' >> ~/.bashrc
|
|
||||||
```
|
|
||||||
2. Then, if you have `~/.profile`, `~/.bash_profile` or `~/.bash_login`, add the commands there as well.
|
|
||||||
If you have none of these, create a `~/.profile` and add the commands there.
|
|
||||||
|
|
||||||
* to add to `~/.profile`:
|
|
||||||
``` bash
|
|
||||||
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.profile
|
|
||||||
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.profile
|
|
||||||
echo 'eval "$(pyenv init - bash)"' >> ~/.profile
|
|
||||||
```
|
|
||||||
* to add to `~/.bash_profile`:
|
|
||||||
```bash
|
|
||||||
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
|
|
||||||
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
|
|
||||||
echo 'eval "$(pyenv init - bash)"' >> ~/.bash_profile
|
|
||||||
```
|
|
||||||
|
|
||||||
**Bash warning**: There are some systems where the `BASH_ENV` variable is configured
|
|
||||||
to point to `.bashrc`. On such systems, you should almost certainly put the
|
|
||||||
`eval "$(pyenv init - bash)"` line into `.bash_profile`, and **not** into `.bashrc`. Otherwise, you
|
|
||||||
may observe strange behaviour, such as `pyenv` getting into an infinite loop.
|
|
||||||
See [#264](https://github.com/pyenv/pyenv/issues/264) for details.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### Zsh
|
|
||||||
|
|
||||||
<details>
|
|
||||||
Add Pyenv startup commands to `~/.zshrc` by running the following in your terminal:
|
|
||||||
|
|
||||||
```zsh
|
|
||||||
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
|
|
||||||
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
|
|
||||||
echo 'eval "$(pyenv init - zsh)"' >> ~/.zshrc
|
|
||||||
```
|
|
||||||
|
|
||||||
If you wish to get Pyenv in noninteractive login shells as well, also add the commands to `~/.zprofile` or `~/.zlogin`.
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### Fish
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
1. If you have Fish 3.2.0 or newer, execute this interactively:
|
|
||||||
```fish
|
|
||||||
set -Ux PYENV_ROOT $HOME/.pyenv
|
|
||||||
test -d $PYENV_ROOT/bin; and fish_add_path $PYENV_ROOT/bin
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Otherwise, execute the snippet below:
|
|
||||||
```fish
|
|
||||||
set -Ux PYENV_ROOT $HOME/.pyenv
|
|
||||||
test -d $PYENV_ROOT/bin; and set -U fish_user_paths $PYENV_ROOT/bin $fish_user_paths
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Now, add this to `~/.config/fish/config.fish`:
|
|
||||||
```fish
|
|
||||||
pyenv init - fish | source
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### Nushell
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
Add the following lines to your `config.nu` to add Pyenv and its shims to your `PATH`.
|
|
||||||
Shell integration (completions and subcommands changing the shell's state)
|
|
||||||
isn't currently supported.
|
|
||||||
|
|
||||||
~~~ nu
|
|
||||||
$env.PYENV_ROOT = "~/.pyenv" | path expand
|
|
||||||
if (( $"($env.PYENV_ROOT)/bin" | path type ) == "dir") {
|
|
||||||
$env.PATH = $env.PATH | prepend $"($env.PYENV_ROOT)/bin" }
|
|
||||||
$env.PATH = $env.PATH | prepend $"(pyenv root)/shims"
|
|
||||||
~~~
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
#### Microsoft PowerShell
|
|
||||||
|
|
||||||
<details>
|
|
||||||
|
|
||||||
Add the commands to `$profile.CurrentUserAllHosts` by running the following in your terminal:
|
|
||||||
|
|
||||||
~~~ pwsh
|
|
||||||
echo '$Env:PYENV_ROOT="$Env:HOME/.pyenv"' >> $profile.CurrentUserAllHosts
|
|
||||||
echo 'if (Test-Path -LP "$Env:PYENV_ROOT/bin" -PathType Container) {
|
|
||||||
$Env:PATH="$Env:PYENV_ROOT/bin:$Env:PATH" }' >> $profile.CurrentUserAllHosts
|
|
||||||
echo 'iex ((pyenv init -) -join "`n")' >> $profile.CurrentUserAllHosts
|
|
||||||
~~~
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### C. Restart your shell
|
### C. Restart your shell
|
||||||
----
|
----
|
||||||
|
|
||||||
|
|
@ -776,7 +674,7 @@ to `PATH` in the `<command>`'s environment, the same as what e.g. RVM does.
|
||||||
### Running nested shells from Python-based programs
|
### Running nested shells from Python-based programs
|
||||||
|
|
||||||
In addition to altering `PATH`, `pyenv exec` sets `PYENV_VERSION` in the
|
In addition to altering `PATH`, `pyenv exec` sets `PYENV_VERSION` in the
|
||||||
executed program's environment to ensure that it won't spontaneouly switch to
|
executed program's environment to ensure that it won't spontaneously switch to
|
||||||
using a different Python version.
|
using a different Python version.
|
||||||
|
|
||||||
Some Python-based programs (e.g. Jupyter) can spawn nested shell sessions.
|
Some Python-based programs (e.g. Jupyter) can spawn nested shell sessions.
|
||||||
|
|
@ -815,18 +713,136 @@ name | default | description
|
||||||
See also [_Special environment variables_ in Python-Build's README](plugins/python-build/README.md#special-environment-variables)
|
See also [_Special environment variables_ in Python-Build's README](plugins/python-build/README.md#special-environment-variables)
|
||||||
for environment variables that can be used to customize the build.
|
for environment variables that can be used to customize the build.
|
||||||
|
|
||||||
|
|
||||||
|
### Manual shell setup
|
||||||
|
|
||||||
|
Below is the suggested shell setup added to shell startup files by `pyenv init --install`.
|
||||||
|
|
||||||
|
* To automatically install Pyenv for a shell different than the running shell, run
|
||||||
|
|
||||||
|
```sh
|
||||||
|
path/to/pyenv --install <shell executable name>
|
||||||
|
```
|
||||||
|
|
||||||
|
e.g. `~/.pyenv --install bash`.
|
||||||
|
|
||||||
|
#### Bash
|
||||||
|
<details>
|
||||||
|
|
||||||
|
Stock Bash startup files vary widely between distributions in which of them source
|
||||||
|
which, under what circumstances, in what order and what additional configuration they perform.
|
||||||
|
As such, the most reliable way to get Pyenv in all environments is to append Pyenv
|
||||||
|
configuration commands to both `.bashrc` (for interactive shells)
|
||||||
|
and the profile file that Bash would use (for login shells).
|
||||||
|
|
||||||
|
1. First, add the commands to `~/.bashrc` by running the following in your terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
|
||||||
|
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
|
||||||
|
echo 'eval "$(pyenv init - bash)"' >> ~/.bashrc
|
||||||
|
```
|
||||||
|
2. Then, if you have `~/.profile`, `~/.bash_profile` or `~/.bash_login`, add the commands there as well.
|
||||||
|
If you have none of these, create a `~/.profile` and add the commands there.
|
||||||
|
|
||||||
|
* to add to `~/.profile`:
|
||||||
|
``` bash
|
||||||
|
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.profile
|
||||||
|
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.profile
|
||||||
|
echo 'eval "$(pyenv init - bash)"' >> ~/.profile
|
||||||
|
```
|
||||||
|
* to add to `~/.bash_profile`:
|
||||||
|
```bash
|
||||||
|
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile
|
||||||
|
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile
|
||||||
|
echo 'eval "$(pyenv init - bash)"' >> ~/.bash_profile
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bash warning**: There are some systems where the `BASH_ENV` variable is configured
|
||||||
|
to point to `.bashrc`. On such systems, you should almost certainly put the
|
||||||
|
`eval "$(pyenv init - bash)"` line into `.bash_profile`, and **not** into `.bashrc`. Otherwise, you
|
||||||
|
may observe strange behaviour, such as `pyenv` getting into an infinite loop.
|
||||||
|
See [#264](https://github.com/pyenv/pyenv/issues/264) for details.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
#### Zsh
|
||||||
|
|
||||||
|
<details>
|
||||||
|
Add Pyenv startup commands to `~/.zshrc` by running the following in your terminal:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
|
||||||
|
echo '[[ -d $PYENV_ROOT/bin ]] && export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
|
||||||
|
echo 'eval "$(pyenv init - zsh)"' >> ~/.zshrc
|
||||||
|
```
|
||||||
|
|
||||||
|
If you wish to get Pyenv in noninteractive login shells as well, also add the commands to `~/.zprofile` or `~/.zlogin`.
|
||||||
|
</details>
|
||||||
|
|
||||||
|
#### Fish
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
1. If you have Fish 3.2.0 or newer, execute this interactively:
|
||||||
|
```fish
|
||||||
|
set -Ux PYENV_ROOT $HOME/.pyenv
|
||||||
|
test -d $PYENV_ROOT/bin; and fish_add_path $PYENV_ROOT/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Otherwise, execute the snippet below:
|
||||||
|
```fish
|
||||||
|
set -Ux PYENV_ROOT $HOME/.pyenv
|
||||||
|
test -d $PYENV_ROOT/bin; and set -U fish_user_paths $PYENV_ROOT/bin $fish_user_paths
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Now, add this to `~/.config/fish/config.fish`:
|
||||||
|
```fish
|
||||||
|
pyenv init - fish | source
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
#### Nushell
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
Add the following lines to your `config.nu` to add Pyenv and its shims to your `PATH`.
|
||||||
|
Shell integration (completions and subcommands changing the shell's state)
|
||||||
|
isn't currently supported.
|
||||||
|
|
||||||
|
~~~ nu
|
||||||
|
$env.PYENV_ROOT = "~/.pyenv" | path expand
|
||||||
|
if (( $"($env.PYENV_ROOT)/bin" | path type ) == "dir") {
|
||||||
|
$env.PATH = $env.PATH | prepend $"($env.PYENV_ROOT)/bin" }
|
||||||
|
$env.PATH = $env.PATH | prepend $"(pyenv root)/shims"
|
||||||
|
~~~
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
#### Microsoft PowerShell
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
Add the commands to `$profile.CurrentUserAllHosts` by running the following in your terminal:
|
||||||
|
|
||||||
|
~~~ pwsh
|
||||||
|
echo '$Env:PYENV_ROOT="$Env:HOME/.pyenv"' >> $profile.CurrentUserAllHosts
|
||||||
|
echo 'if (Test-Path -LP "$Env:PYENV_ROOT/bin" -PathType Container) {
|
||||||
|
$Env:PATH="$Env:PYENV_ROOT/bin:$Env:PATH" }' >> $profile.CurrentUserAllHosts
|
||||||
|
echo 'iex ((pyenv init -) -join "`n")' >> $profile.CurrentUserAllHosts
|
||||||
|
~~~
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
The pyenv source code is [hosted on
|
The pyenv source code is [hosted on
|
||||||
GitHub](https://github.com/pyenv/pyenv). It's clean, modular,
|
GitHub](https://github.com/pyenv/pyenv).
|
||||||
and easy to understand, even if you're not a shell hacker.
|
|
||||||
|
|
||||||
Tests are executed using [Bats](https://github.com/bats-core/bats-core):
|
Tests are executed using [Bats](https://github.com/bats-core/bats-core).
|
||||||
|
See the [tests README](test/README.md) for details.
|
||||||
bats test
|
|
||||||
bats/test/<file>.bats
|
|
||||||
|
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
set -e
|
set -e
|
||||||
[ -n "$PYENV_DEBUG" ] && set -x
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
version="2.7.1"
|
version="2.8.5"
|
||||||
git_revision=""
|
git_revision=""
|
||||||
|
|
||||||
if cd "${BASH_SOURCE%/*}" 2>/dev/null && git remote -v 2>/dev/null | grep -q pyenv; then
|
if cd "${BASH_SOURCE%/*}" 2>/dev/null && git remote -v 2>/dev/null | grep -q pyenv; then
|
||||||
|
|
|
||||||
|
|
@ -36,12 +36,16 @@ if [ -n "$versions" ]; then
|
||||||
pyenv-version-file-write "$PYENV_VERSION_FILE" "${versions[@]}"
|
pyenv-version-file-write "$PYENV_VERSION_FILE" "${versions[@]}"
|
||||||
else
|
else
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
|
# VAR=() of unquoted value does glob expansion (against the current dir's contents) after IFS-splitting
|
||||||
|
# which is undesired
|
||||||
|
set -f
|
||||||
IFS=: versions=($(
|
IFS=: versions=($(
|
||||||
pyenv-version-file-read "$PYENV_VERSION_FILE" ||
|
pyenv-version-file-read "$PYENV_VERSION_FILE" ||
|
||||||
pyenv-version-file-read "${PYENV_ROOT}/global" ||
|
pyenv-version-file-read "${PYENV_ROOT}/global" ||
|
||||||
pyenv-version-file-read "${PYENV_ROOT}/default" ||
|
pyenv-version-file-read "${PYENV_ROOT}/default" ||
|
||||||
echo system
|
echo system
|
||||||
))
|
))
|
||||||
|
set +f
|
||||||
IFS="$OLDIFS"
|
IFS="$OLDIFS"
|
||||||
for version in "${versions[@]}"; do
|
for version in "${versions[@]}"; do
|
||||||
echo "$version"
|
echo "$version"
|
||||||
|
|
|
||||||
|
|
@ -473,12 +473,12 @@ function pyenv {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ("${commands[*]}" -split ' ') -contains \$command ) {
|
if ( ("${commands[*]}" -split ' ') -contains \$command ) {
|
||||||
\$shell_cmds = (& (get-command -commandtype application pyenv) sh-\$command \$args)
|
\$shell_cmds = (& (get-command -commandtype application pyenv -totalcount 1) sh-\$command \$args)
|
||||||
if ( \$shell_cmds.Count -gt 0 ) {
|
if ( \$shell_cmds.Count -gt 0 ) {
|
||||||
iex (\$shell_cmds -join "\`n")
|
iex (\$shell_cmds -join "\`n")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
& (get-command -commandtype application pyenv) \$command \$args
|
& (get-command -commandtype application pyenv -totalcount 1) \$command \$args
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOS
|
EOS
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,11 @@ elif [ -n "$versions" ]; then
|
||||||
pyenv-version-file-write ${FORCE:+-f }.python-version "${versions[@]}"
|
pyenv-version-file-write ${FORCE:+-f }.python-version "${versions[@]}"
|
||||||
else
|
else
|
||||||
if version_file="$(pyenv-version-file "$PWD")"; then
|
if version_file="$(pyenv-version-file "$PWD")"; then
|
||||||
|
# VAR=() of unquoted value does glob expansion (against the current dir's contents) after IFS-splitting
|
||||||
|
# which is undesired
|
||||||
|
set -f
|
||||||
IFS=: versions=($(pyenv-version-file-read "$version_file"))
|
IFS=: versions=($(pyenv-version-file-read "$version_file"))
|
||||||
|
set +f
|
||||||
for version in "${versions[@]}"; do
|
for version in "${versions[@]}"; do
|
||||||
echo "$version"
|
echo "$version"
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,9 @@ fi
|
||||||
PYENV_PREFIX_PATHS=()
|
PYENV_PREFIX_PATHS=()
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
{ IFS=:
|
{ IFS=:
|
||||||
|
# Unquoted $VAR does glob expansion (against the current dir's contents) after IFS-splitting
|
||||||
|
# which is undesired
|
||||||
|
set -f
|
||||||
for version in ${PYENV_VERSION}; do
|
for version in ${PYENV_VERSION}; do
|
||||||
if [ "$version" = "system" ]; then
|
if [ "$version" = "system" ]; then
|
||||||
if PYTHON_PATH="$(PYENV_VERSION="${version}" pyenv-which python --skip-advice 2>/dev/null)" || \
|
if PYTHON_PATH="$(PYENV_VERSION="${version}" pyenv-which python --skip-advice 2>/dev/null)" || \
|
||||||
|
|
@ -52,6 +55,7 @@ OLDIFS="$IFS"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
set +f
|
||||||
}
|
}
|
||||||
IFS="$OLDIFS"
|
IFS="$OLDIFS"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,15 +13,31 @@ mkdir -p "$SHIM_PATH"
|
||||||
declare last_acquire_error
|
declare last_acquire_error
|
||||||
|
|
||||||
acquire_lock() {
|
acquire_lock() {
|
||||||
# Ensure only one instance of pyenv-rehash is running at a time by
|
|
||||||
# setting the shell's `noclobber` option and attempting to write to
|
|
||||||
# the prototype shim file.
|
|
||||||
local ret
|
local ret
|
||||||
|
# An old lock file is presumed stale. We assume no healthy rehash takes this long.
|
||||||
|
# The time is picked very small so that a killed rehash holds up new shell sessions
|
||||||
|
# for as little as possible
|
||||||
|
find "$PROTOTYPE_SHIM_PATH" -mmin +2 -exec rm -f {} \; 2>/dev/null || true
|
||||||
set -o noclobber
|
set -o noclobber
|
||||||
# Assuming an old lockfile is stale
|
|
||||||
# Unknown why this happens for some users but this at least unblocks them
|
|
||||||
last_acquire_error="$( { ( echo -n > "$PROTOTYPE_SHIM_PATH"; ) 2>&1 1>&3 3>&1-; } 3>&1)" \
|
last_acquire_error="$( { ( echo -n > "$PROTOTYPE_SHIM_PATH"; ) 2>&1 1>&3 3>&1-; } 3>&1)" \
|
||||||
|| { find "$PROTOTYPE_SHIM_PATH" -mmin +10 -exec rm -f {} \; ; ret=1; }
|
&& trap release_lock EXIT \
|
||||||
|
|| {
|
||||||
|
# Linux Landlock and MacOS Seatbelt sandbox subsystems return false information in access(),
|
||||||
|
# making -w "$SHIM_PATH" not catch the fact that the shims dir is not writable in this case.
|
||||||
|
# Bash doesn't provide access to errno to check for non-EEXIST error code in `echo >'.
|
||||||
|
# So check for writablity by trying to write to a different file,
|
||||||
|
# in a way that taxes the usual use case as little as possible.
|
||||||
|
if [[ -z $tested_for_other_write_errors ]]; then
|
||||||
|
# if lots of (50+) rehashes run concurrently, another concurrent rehash
|
||||||
|
# may delete the temporary file in remove_*_shims() before we do so
|
||||||
|
( t="$(TMPDIR="$SHIM_PATH" mktemp)" && rm -f "$t" ) \
|
||||||
|
&& tested_for_other_write_errors=1 \
|
||||||
|
|| { echo "pyenv: cannot rehash: $SHIM_PATH isn't writable" >&2
|
||||||
|
set +o noclobber
|
||||||
|
exit 1; }
|
||||||
|
fi
|
||||||
|
ret=1
|
||||||
|
}
|
||||||
set +o noclobber
|
set +o noclobber
|
||||||
[[ -z "${ret}" ]]
|
[[ -z "${ret}" ]]
|
||||||
}
|
}
|
||||||
|
|
@ -32,6 +48,7 @@ remove_prototype_shim() {
|
||||||
|
|
||||||
release_lock() {
|
release_lock() {
|
||||||
remove_prototype_shim
|
remove_prototype_shim
|
||||||
|
trap - EXIT
|
||||||
}
|
}
|
||||||
|
|
||||||
if [ ! -w "$SHIM_PATH" ]; then
|
if [ ! -w "$SHIM_PATH" ]; then
|
||||||
|
|
@ -45,22 +62,8 @@ PYENV_REHASH_TIMEOUT=${PYENV_REHASH_TIMEOUT:-60}
|
||||||
while (( SECONDS <= start + PYENV_REHASH_TIMEOUT )); do
|
while (( SECONDS <= start + PYENV_REHASH_TIMEOUT )); do
|
||||||
if acquire_lock; then
|
if acquire_lock; then
|
||||||
acquired=1
|
acquired=1
|
||||||
|
|
||||||
# If we were able to obtain a lock, register a trap to clean up the
|
|
||||||
# prototype shim when the process exits.
|
|
||||||
trap release_lock EXIT
|
|
||||||
|
|
||||||
break
|
break
|
||||||
else
|
else
|
||||||
#Landlock sandbox subsystem in the Linux kernel returns false information in access() as of 6.14.0,
|
|
||||||
# making -w "$SHIM_PATH" not catch the fact that the shims dir is not writable in this case.
|
|
||||||
#Bash doesn't provide access to errno to check for non-EEXIST error code in acquire_lock.
|
|
||||||
#So check for writablity by trying to write to a different file,
|
|
||||||
# in a way that taxes the usual use case as little as possible.
|
|
||||||
if [[ -z $tested_for_other_write_errors ]]; then
|
|
||||||
( t="$(TMPDIR="$SHIM_PATH" mktemp)" && rm "$t" ) && tested_for_other_write_errors=1 ||
|
|
||||||
{ echo "pyenv: cannot rehash: $SHIM_PATH isn't writable" >&2; break; }
|
|
||||||
fi
|
|
||||||
# POSIX sleep(1) doesn't provide subsecond precision, but many others do
|
# POSIX sleep(1) doesn't provide subsecond precision, but many others do
|
||||||
sleep 0.1 2>/dev/null || sleep 1
|
sleep 0.1 2>/dev/null || sleep 1
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,11 @@ set -e
|
||||||
|
|
||||||
exitcode=0
|
exitcode=0
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
|
# VAR=() of unquoted value does glob expansion (against the current dir's contents) after IFS-splitting
|
||||||
|
# which is undesired
|
||||||
|
set -f
|
||||||
IFS=: PYENV_VERSION_NAMES=($(pyenv-version-name)) || exitcode=$?
|
IFS=: PYENV_VERSION_NAMES=($(pyenv-version-name)) || exitcode=$?
|
||||||
|
set +f
|
||||||
IFS="$OLDIFS"
|
IFS="$OLDIFS"
|
||||||
|
|
||||||
unset bare
|
unset bare
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,28 @@ target_dir="$1"
|
||||||
|
|
||||||
find_local_version_file() {
|
find_local_version_file() {
|
||||||
local root="$1"
|
local root="$1"
|
||||||
while ! [[ "$root" =~ ^//[^/]*$ ]]; do
|
# Nonexistent paths are UB as of this writing.
|
||||||
if [ -f "${root}/.python-version" ]; then
|
# Relative ones cause a failure but absolute ones don't
|
||||||
echo "${root}/.python-version"
|
[[ $root != /* ]] && root=$(CDPATH= cd -- "$root" && pwd)
|
||||||
|
# Original Rbenv code supports UNC notation for Cygwin/MinGW
|
||||||
|
# (https://github.com/rbenv/rbenv/pull/529)
|
||||||
|
# POSIX.1-2024 still allows to treat //<name> in implementation-specific manner
|
||||||
|
# (https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/V1_chap04.html#tag_04_16)
|
||||||
|
# even though few UNIX variants do that
|
||||||
|
local unc; [[ $root =~ ^//[^/] && ! / -ef // ]] && unc=1
|
||||||
|
root="${root%/}"
|
||||||
|
# when testing root, $root is ""
|
||||||
|
while true; do
|
||||||
|
# don't test //.python-version if // is special
|
||||||
|
# as it's pointless and possibly very slow
|
||||||
|
# if it e.g. leads to a network search
|
||||||
|
[[ $unc && $root == / ]] && break
|
||||||
|
|
||||||
|
if [[ -f $root/.python-version ]]; then
|
||||||
|
echo "$root/.python-version"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
[ -n "$root" ] || break
|
[[ -n $root ]] || break
|
||||||
root="${root%/*}"
|
root="${root%/*}"
|
||||||
done
|
done
|
||||||
return 1
|
return 1
|
||||||
|
|
@ -22,7 +38,9 @@ find_local_version_file() {
|
||||||
if [ -n "$target_dir" ]; then
|
if [ -n "$target_dir" ]; then
|
||||||
find_local_version_file "$target_dir"
|
find_local_version_file "$target_dir"
|
||||||
else
|
else
|
||||||
find_local_version_file "$PYENV_DIR" || {
|
# In tests, PYENV_DIR is not set, ultimately leading to "cd: null directory" in OpenSUSE
|
||||||
|
# While most `cd` implementations treat an empty argument the same as missing, that's still wrong
|
||||||
|
find_local_version_file "${PYENV_DIR:=$PWD}" || {
|
||||||
[ "$PYENV_DIR" != "$PWD" ] && find_local_version_file "$PWD"
|
[ "$PYENV_DIR" != "$PWD" ] && find_local_version_file "$PWD"
|
||||||
} || echo "${PYENV_ROOT}/version"
|
} || echo "${PYENV_ROOT}/version"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -46,17 +46,22 @@ versions=()
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
{ IFS=:
|
{ IFS=:
|
||||||
any_not_installed=0
|
any_not_installed=0
|
||||||
|
normalization_done=
|
||||||
|
# Unquoted $VAR does glob expansion (against the current dir's contents) after IFS-splitting
|
||||||
|
# which is undesired
|
||||||
|
set -f
|
||||||
for version in ${PYENV_VERSION}; do
|
for version in ${PYENV_VERSION}; do
|
||||||
# Remove the explicit 'python-' prefix from versions like 'python-3.12'.
|
# Remove the explicit 'python-' prefix from versions like 'python-3.12'.
|
||||||
normalised_version="${version#python-}"
|
normalised_version="${version#python-}"
|
||||||
if version_exists "${version}" || [ "$version" = "system" ]; then
|
[[ $version != "${normalised_version}" ]] && normalization_done=1
|
||||||
versions+=("${version}")
|
if [[ $version == "system" ]] || version_exists "${normalised_version}" ; then
|
||||||
elif version_exists "${normalised_version}"; then
|
|
||||||
versions+=("${normalised_version}")
|
versions+=("${normalised_version}")
|
||||||
elif resolved_version="$(pyenv-latest -b "${version}")"; then
|
elif [[ -n $normalization_done ]] && version_exists "${version}"; then
|
||||||
versions+=("${resolved_version}")
|
versions+=("${version}")
|
||||||
elif resolved_version="$(pyenv-latest -b "${normalised_version}")"; then
|
elif resolved_version="$(pyenv-latest -b "${normalised_version}")"; then
|
||||||
versions+=("${resolved_version}")
|
versions+=("${resolved_version}")
|
||||||
|
elif [[ -n $normalization_done ]] && resolved_version="$(pyenv-latest -b "${version}")"; then
|
||||||
|
versions+=("${resolved_version}")
|
||||||
else
|
else
|
||||||
if [[ -n $FORCE ]]; then
|
if [[ -n $FORCE ]]; then
|
||||||
versions+=("${normalised_version}")
|
versions+=("${normalised_version}")
|
||||||
|
|
@ -66,6 +71,7 @@ OLDIFS="$IFS"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
set +f
|
||||||
}
|
}
|
||||||
IFS="$OLDIFS"
|
IFS="$OLDIFS"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,9 @@ else
|
||||||
miss_prefix=" "
|
miss_prefix=" "
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
IFS=:
|
IFS=:
|
||||||
|
# VAR=() of unquoted value does glob expansion (against the current dir's contents) after IFS-splitting
|
||||||
|
# which is undesired
|
||||||
|
set -f
|
||||||
if ((${BASH_VERSINFO[0]} > 3)); then
|
if ((${BASH_VERSINFO[0]} > 3)); then
|
||||||
for i in $(pyenv-version-name || true); do
|
for i in $(pyenv-version-name || true); do
|
||||||
current_versions["$i"]="1"
|
current_versions["$i"]="1"
|
||||||
|
|
@ -106,6 +109,7 @@ else
|
||||||
else
|
else
|
||||||
current_versions=($(pyenv-version-name || true))
|
current_versions=($(pyenv-version-name || true))
|
||||||
fi
|
fi
|
||||||
|
set +f
|
||||||
IFS="$OLDIFS"
|
IFS="$OLDIFS"
|
||||||
include_system="1"
|
include_system="1"
|
||||||
fi
|
fi
|
||||||
|
|
@ -161,11 +165,15 @@ versions_dir_entries=("$versions_dir"/*)
|
||||||
if sort --version-sort </dev/null >/dev/null 2>&1; then
|
if sort --version-sort </dev/null >/dev/null 2>&1; then
|
||||||
# system sort supports version sorting
|
# system sort supports version sorting
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
|
# VAR=() of unquoted value does glob expansion (against the current dir's contents) after IFS-splitting
|
||||||
|
# which is undesired
|
||||||
|
set -f
|
||||||
IFS=$'\n'
|
IFS=$'\n'
|
||||||
versions_dir_entries=($(
|
versions_dir_entries=($(
|
||||||
printf "%s\n" "${versions_dir_entries[@]}" |
|
printf "%s\n" "${versions_dir_entries[@]}" |
|
||||||
sort --version-sort
|
sort --version-sort
|
||||||
))
|
))
|
||||||
|
set +f
|
||||||
IFS="$OLDIFS"
|
IFS="$OLDIFS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,11 @@ if [ -z "$PYENV_COMMAND" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
|
# Unquoted $VAR or VAR=() of unquoted value does glob expansion (against the current dir's contents)
|
||||||
|
# after IFS-splitting which is undesired
|
||||||
|
set -f
|
||||||
IFS=: versions=(${PYENV_VERSION:-$(pyenv-version-name -f)})
|
IFS=: versions=(${PYENV_VERSION:-$(pyenv-version-name -f)})
|
||||||
|
set +f
|
||||||
IFS="$OLDIFS"
|
IFS="$OLDIFS"
|
||||||
|
|
||||||
declare -a nonexistent_versions
|
declare -a nonexistent_versions
|
||||||
|
|
|
||||||
2
plugins/.gitignore
vendored
2
plugins/.gitignore
vendored
|
|
@ -2,4 +2,6 @@
|
||||||
!/.gitignore
|
!/.gitignore
|
||||||
!/version-ext-compat
|
!/version-ext-compat
|
||||||
!/python-build
|
!/python-build
|
||||||
|
!/pyenv-binary
|
||||||
|
!/pyenv-link
|
||||||
/python-build/test/build
|
/python-build/test/build
|
||||||
|
|
|
||||||
94
plugins/pyenv-binary/README.md
Normal file
94
plugins/pyenv-binary/README.md
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
# pyenv-binary (experimental)
|
||||||
|
|
||||||
|
Package an installed Python version into a relocatable archive that can be
|
||||||
|
installed on another machine.
|
||||||
|
|
||||||
|
This is experimental and intentionally decoupled: it does not change
|
||||||
|
`pyenv install` or any other command. You drive it explicitly through
|
||||||
|
`pyenv binary`. Run `pyenv binary <command> --help` for details on a command.
|
||||||
|
|
||||||
|
## Portability
|
||||||
|
|
||||||
|
An archive is portable across machines that share its build platform (OS,
|
||||||
|
architecture and a compatible libc) and have the recorded system libraries. It
|
||||||
|
is not portable across, say, glibc and musl, or to an older glibc; the platform
|
||||||
|
and dependency metadata exist to catch that.
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### `pyenv binary package [-v|--verbose] <version>[:<entry>] --archive-base-url <url>`
|
||||||
|
|
||||||
|
Installs `<version>` from source under a separate name, packages that install
|
||||||
|
with `save`, then emits a python-build definition for it with
|
||||||
|
`generate-installer`. With no explicit entry, the name is generated from the
|
||||||
|
current platform, platform version and architecture. An explicit entry keeps
|
||||||
|
the existing custom-build workflow.
|
||||||
|
|
||||||
|
Pass `-v` to show build progress from `pyenv install`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv binary package 3.12.7 \
|
||||||
|
--archive-base-url https://example.com/binaries
|
||||||
|
# On Debian 12 x86_64, writes 3.12.7-debian-12-x86_64.tar.gz,
|
||||||
|
# its .meta file and a `3.12.7-debian-12-x86_64' definition.
|
||||||
|
|
||||||
|
pyenv binary package 3.12.7:company-python \
|
||||||
|
--archive-base-url https://example.com/binaries
|
||||||
|
```
|
||||||
|
|
||||||
|
The archive, metadata and definition land in the current directory, named after
|
||||||
|
the entry. Host the archive under `<url>` and drop the definition into
|
||||||
|
python-build's definition directory.
|
||||||
|
|
||||||
|
### `pyenv binary package-name <version>`
|
||||||
|
|
||||||
|
Prints the automatically generated entry name without building anything. Linux
|
||||||
|
uses the distribution name and version, macOS uses the macOS version, and other
|
||||||
|
systems use the name and release reported by `uname`. All names include the
|
||||||
|
architecture.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv binary package-name 3.12.7
|
||||||
|
# 3.12.7-debian-12-x86_64
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pyenv binary save <version> [<output-dir>] [--name <name>]`
|
||||||
|
|
||||||
|
Packs an installed version into `<version>-<platform>.tar.gz` (relative paths)
|
||||||
|
and writes `<version>-<platform>.meta` describing the build platform (OS, arch,
|
||||||
|
distro and libc version) and the system libraries the build links against. Use
|
||||||
|
`--name` to set a different base name for both files.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv binary save 3.12.7 ./dist
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pyenv binary generate-installer <metadata-file> --archive-url <url> [-o <output>]`
|
||||||
|
|
||||||
|
Reads a `.meta` file and emits a python-build definition. Drop it into
|
||||||
|
python-build's definition directory and `pyenv install <name>` installs the
|
||||||
|
archive like any other version. The archive location is a parameter, so you can
|
||||||
|
host it anywhere (it does not have to be a pyenv location); the archive itself
|
||||||
|
must sit next to the `.meta` file so its checksum can be baked into the
|
||||||
|
definition.
|
||||||
|
|
||||||
|
The definition refuses to install on a different OS/architecture, or an older
|
||||||
|
glibc, than the archive was built for, and checks that the system libraries it
|
||||||
|
needs are present.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pyenv binary generate-installer ./dist/3.12.7-linux-x86_64.meta \
|
||||||
|
--archive-url https://example.com/3.12.7-linux-x86_64.tar.gz \
|
||||||
|
-o "$(pyenv root)/plugins/python-build/share/python-build/3.12.7-linux-x86_64"
|
||||||
|
|
||||||
|
pyenv install 3.12.7-linux-x86_64
|
||||||
|
```
|
||||||
|
|
||||||
|
### `pyenv binary relocate <prefix>`
|
||||||
|
|
||||||
|
Rewrites the rpaths of a Python tree unpacked into `<prefix>` so the interpreter
|
||||||
|
and its extension modules load the bundled libraries from there rather than from
|
||||||
|
the path the archive was built at. Uses `patchelf`. The generated definition
|
||||||
|
calls this; you rarely run it by hand.
|
||||||
|
|
||||||
|
Relocation is implemented for Linux; macOS is not wired up yet.
|
||||||
65
plugins/pyenv-binary/bin/pyenv-binary
Executable file
65
plugins/pyenv-binary/bin/pyenv-binary
Executable file
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Package an installed Python version as a relocatable binary (experimental)
|
||||||
|
#
|
||||||
|
# Usage: pyenv binary <command> [<args>]
|
||||||
|
#
|
||||||
|
# `pyenv binary` packages an already-installed Python version into a relocatable
|
||||||
|
# archive that can be installed on another machine. It is experimental and does
|
||||||
|
# not touch `pyenv install' or any other command.
|
||||||
|
#
|
||||||
|
# Run `pyenv binary' to list its commands, or `pyenv binary <command> --help'
|
||||||
|
# for command-specific help.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
libexec="${BASH_SOURCE%/*}/../libexec"
|
||||||
|
|
||||||
|
# The pyenv launcher only puts a plugin's bin on PATH, but `pyenv-help' looks a
|
||||||
|
# command up there. Add our libexec so the subcommands, and the help text they
|
||||||
|
# print, can be found.
|
||||||
|
export PATH="${libexec}:${PATH}"
|
||||||
|
|
||||||
|
list_commands() {
|
||||||
|
local path
|
||||||
|
for path in "$libexec"/pyenv-binary-*; do
|
||||||
|
[ -e "$path" ] && echo "${path##*/pyenv-binary-}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Provide pyenv completions
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
shift
|
||||||
|
if [ -z "$1" ]; then
|
||||||
|
list_commands
|
||||||
|
else
|
||||||
|
command_path="${libexec}/pyenv-binary-$1"
|
||||||
|
shift
|
||||||
|
[ -x "$command_path" ] && exec "$command_path" --complete "$@"
|
||||||
|
fi
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
subcommand="$1"
|
||||||
|
if [ -z "$subcommand" ]; then
|
||||||
|
{ pyenv-help binary
|
||||||
|
echo
|
||||||
|
echo "Commands:"
|
||||||
|
list_commands | sed 's/^/ /'
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
command_path="${libexec}/pyenv-binary-${subcommand}"
|
||||||
|
if [ ! -x "$command_path" ]; then
|
||||||
|
echo "pyenv-binary: no such command \`${subcommand}'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||||
|
exec pyenv-help "binary-${subcommand}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec "$command_path" "$@"
|
||||||
224
plugins/pyenv-binary/libexec/pyenv-binary-generate-installer
Executable file
224
plugins/pyenv-binary/libexec/pyenv-binary-generate-installer
Executable file
|
|
@ -0,0 +1,224 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Generate a python-build definition for a saved binary archive
|
||||||
|
#
|
||||||
|
# Usage: pyenv binary generate-installer <metadata-file> --archive-url <url> [-o <output>]
|
||||||
|
#
|
||||||
|
# Reads a metadata file written by `pyenv binary save' and emits a python-build
|
||||||
|
# definition. Dropped into python-build's definition directory, it installs the
|
||||||
|
# archive like any other version: `pyenv install' downloads it from <url>,
|
||||||
|
# checks the platform and the required system libraries, unpacks it, then
|
||||||
|
# rewrites rpaths so the copy runs from its prefix.
|
||||||
|
#
|
||||||
|
# The archive must sit next to the metadata file so its checksum can be baked
|
||||||
|
# into the definition; `pyenv binary save' writes the two together.
|
||||||
|
#
|
||||||
|
# <metadata-file> A .meta file written by `pyenv binary save'.
|
||||||
|
# The corresponding binary package written by `pyenv binary save'
|
||||||
|
# needs to be present alongside it.
|
||||||
|
# --archive-url <url> The URL for the resulting installation script to download
|
||||||
|
# the package from.
|
||||||
|
# -o <output> Write the definition here (default: stdout).
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
# Provide pyenv completions
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
echo --archive-url
|
||||||
|
echo -o
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
metadata=""
|
||||||
|
archive_url=""
|
||||||
|
output=""
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--archive-url )
|
||||||
|
[ $# -ge 2 ] || { echo "pyenv-binary: --archive-url needs a value" >&2; exit 1; }
|
||||||
|
archive_url="$2"; shift 2 ;;
|
||||||
|
-o )
|
||||||
|
[ $# -ge 2 ] || { echo "pyenv-binary: -o needs a value" >&2; exit 1; }
|
||||||
|
output="$2"; shift 2 ;;
|
||||||
|
-* )
|
||||||
|
echo "pyenv-binary: unknown option \`$1'" >&2; exit 1 ;;
|
||||||
|
* )
|
||||||
|
[ -z "$metadata" ] || { echo "pyenv-binary: unexpected argument \`$1'" >&2; exit 1; }
|
||||||
|
metadata="$1"; shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$metadata" ] || [ -z "$archive_url" ]; then
|
||||||
|
pyenv-help --usage binary-generate-installer >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
version="" os="" arch="" distro="" libc="" build_prefix="" archive=""
|
||||||
|
deps=""
|
||||||
|
while IFS='=' read -r key value; do
|
||||||
|
case "$key" in
|
||||||
|
version ) version="$value" ;;
|
||||||
|
os ) os="$value" ;;
|
||||||
|
arch ) arch="$value" ;;
|
||||||
|
distro ) distro="$value" ;;
|
||||||
|
libc ) libc="$value" ;;
|
||||||
|
build_prefix ) build_prefix="$value" ;;
|
||||||
|
archive ) archive="$value" ;;
|
||||||
|
dep ) deps="${deps:+$deps }$value" ;;
|
||||||
|
esac
|
||||||
|
done < "$metadata"
|
||||||
|
|
||||||
|
# Fail here if the metadata is incomplete, rather than bake a blank into the
|
||||||
|
# definition where a missing arch would give a confusing platform error.
|
||||||
|
for field in version os arch archive; do
|
||||||
|
if [ -z "${!field}" ]; then
|
||||||
|
echo "pyenv-binary: metadata is missing \`${field}'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Mach-O load commands contain the prefix where Python was built.
|
||||||
|
if [ "$os" = "Darwin" ] && [ -z "$build_prefix" ]; then
|
||||||
|
echo "pyenv-binary: metadata is missing \`build_prefix'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The glibc floor only applies on Linux; require it there so the definition can
|
||||||
|
# tell whether the target is new enough to run the binaries.
|
||||||
|
if [ "$os" = "Linux" ] && [ -z "$libc" ]; then
|
||||||
|
echo "pyenv-binary: metadata is missing \`libc'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# python-build downloads the archive itself, so the checksum has to be baked in.
|
||||||
|
# The archive sits next to the metadata `save' wrote it alongside.
|
||||||
|
archive_path="$(dirname "$metadata")/${archive}"
|
||||||
|
if [ ! -r "$archive_path" ]; then
|
||||||
|
echo "pyenv-binary: cannot read the archive \`${archive_path}' to checksum it" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if command -v sha256sum >/dev/null 2>&1; then
|
||||||
|
sha256="$(sha256sum "$archive_path")"; sha256="${sha256%% *}"
|
||||||
|
elif command -v shasum >/dev/null 2>&1; then
|
||||||
|
sha256="$(shasum -a 256 "$archive_path")"; sha256="${sha256%% *}"
|
||||||
|
elif command -v openssl >/dev/null 2>&1; then
|
||||||
|
sha256="$(openssl dgst -sha256 "$archive_path")"; sha256="${sha256##* }"
|
||||||
|
else
|
||||||
|
echo "pyenv-binary: need sha256sum, shasum or openssl to checksum the archive" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
emit() {
|
||||||
|
# The values known now go in as %q-quoted assignments so a stray quote or space
|
||||||
|
# in the URL or metadata cannot break the definition or inject into it. The body
|
||||||
|
# below is a quoted here-doc, verbatim.
|
||||||
|
{
|
||||||
|
printf '# python-build definition for prebuilt Python %s (%s/%s%s).\n' \
|
||||||
|
"$version" "$os" "$arch" "${distro:+, $distro}"
|
||||||
|
echo "# Generated by \`pyenv binary generate-installer'."
|
||||||
|
echo
|
||||||
|
printf 'EXPECT_OS=%q\n' "$os"
|
||||||
|
printf 'EXPECT_ARCH=%q\n' "$arch"
|
||||||
|
printf 'EXPECT_LIBC=%q\n' "$libc"
|
||||||
|
printf 'BUILD_PREFIX=%q\n' "$build_prefix"
|
||||||
|
printf 'VERSION=%q\n' "$version"
|
||||||
|
printf 'ARCHIVE_URL=%q\n' "$archive_url"
|
||||||
|
printf 'SHA256=%q\n' "$sha256"
|
||||||
|
printf 'DEPS=%q\n' "$deps"
|
||||||
|
}
|
||||||
|
cat <<'EOF'
|
||||||
|
|
||||||
|
os="$(uname -s)"
|
||||||
|
arch="$(uname -m)"
|
||||||
|
if [ "$os" != "$EXPECT_OS" ] || [ "$arch" != "$EXPECT_ARCH" ]; then
|
||||||
|
echo "pyenv-binary: this archive is for ${EXPECT_OS}/${EXPECT_ARCH}, not ${os}/${arch}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Refuse a target whose glibc is older than the one the archive was built on;
|
||||||
|
# the dynamic loader would reject the binaries.
|
||||||
|
case "$EXPECT_LIBC" in
|
||||||
|
"glibc "* )
|
||||||
|
build_libc="${EXPECT_LIBC#glibc }"
|
||||||
|
target_libc="$(getconf GNU_LIBC_VERSION 2>/dev/null || true)"
|
||||||
|
case "$target_libc" in
|
||||||
|
"glibc "* )
|
||||||
|
target_libc="${target_libc#glibc }"
|
||||||
|
older="$(printf '%s\n%s\n' "$build_libc" "$target_libc" | sort -V | head -n1)"
|
||||||
|
if [ "$older" = "$target_libc" ] && [ "$target_libc" != "$build_libc" ]; then
|
||||||
|
echo "pyenv-binary: archive needs glibc ${build_libc} or newer, but this system has ${target_libc}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Required system libraries must already be present on the target. Linux lists
|
||||||
|
# the soname first; FreeBSD lists it in the resolved path, so normalize that
|
||||||
|
# path before matching exact names.
|
||||||
|
cache=""
|
||||||
|
if command -v ldconfig &>/dev/null; then
|
||||||
|
case "$os" in
|
||||||
|
FreeBSD )
|
||||||
|
cache="$(ldconfig -r 2>/dev/null)" &&
|
||||||
|
cache="$(printf '%s\n' "$cache" | awk -F ' *=> *' '{ sub(/^.*\//, "", $2); print $2 }')" \
|
||||||
|
|| cache=""
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
cache="$(ldconfig -p 2>/dev/null)" || cache=""
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
if [ -n "$cache" ]; then
|
||||||
|
missing=""
|
||||||
|
for dep in $DEPS; do
|
||||||
|
printf '%s\n' "$cache" | awk -v d="$dep" '$1 == d { found = 1 } END { exit !found }' \
|
||||||
|
|| missing="${missing} ${dep}"
|
||||||
|
done
|
||||||
|
if [ -n "$missing" ]; then
|
||||||
|
echo "pyenv-binary: missing required system libraries:${missing}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
elif [ -n "$DEPS" ]; then
|
||||||
|
echo "pyenv-binary: cannot check required system libraries (ldconfig cache unavailable)" >&2
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$os" in
|
||||||
|
Darwin )
|
||||||
|
for tool in otool install_name_tool; do
|
||||||
|
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||||
|
echo "pyenv-binary: need ${tool} to relocate the binary" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
if ! command -v patchelf >/dev/null 2>&1; then
|
||||||
|
echo "pyenv-binary: need patchelf to relocate the binary" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
build_package_relocate() {
|
||||||
|
if [ "$os" = "Darwin" ]; then
|
||||||
|
pyenv binary relocate "$PREFIX_PATH" "$BUILD_PREFIX"
|
||||||
|
else
|
||||||
|
pyenv binary relocate "$PREFIX_PATH"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
install_package "Python-${VERSION}-binary" "${ARCHIVE_URL}#${SHA256}" copy relocate
|
||||||
|
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$output" ]; then
|
||||||
|
emit > "$output"
|
||||||
|
echo "Wrote definition to ${output}"
|
||||||
|
else
|
||||||
|
emit
|
||||||
|
fi
|
||||||
83
plugins/pyenv-binary/libexec/pyenv-binary-package
Executable file
83
plugins/pyenv-binary/libexec/pyenv-binary-package
Executable file
|
|
@ -0,0 +1,83 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Create an installable binary package from a Python version
|
||||||
|
#
|
||||||
|
# Usage: pyenv binary package [-v|--verbose] <version>[:<entry>] --archive-base-url <url>
|
||||||
|
#
|
||||||
|
# Installs <version> from source under a separate entry name, saves it as a
|
||||||
|
# binary package, then emits a python-build definition for that package.
|
||||||
|
# The archive, metadata and definition are written to the current directory
|
||||||
|
# as <entry>.tar.gz, <entry>.meta and <entry>.
|
||||||
|
#
|
||||||
|
# <version> A version `pyenv install' knows how to build.
|
||||||
|
# <entry> The optional name to build under and install the
|
||||||
|
# binary as. If omitted, a name is generated from the
|
||||||
|
# current platform, platform version and architecture.
|
||||||
|
# -v,--verbose Show build progress from `pyenv install'.
|
||||||
|
# --archive-base-url <url>
|
||||||
|
# Where the archive will be hosted; the definition
|
||||||
|
# downloads it from <url>/<entry>.tar.gz.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
# Provide pyenv completions
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
echo --archive-base-url
|
||||||
|
echo --verbose
|
||||||
|
exec pyenv-install --list --bare
|
||||||
|
fi
|
||||||
|
|
||||||
|
spec=""
|
||||||
|
archive_base_url=""
|
||||||
|
verbose=""
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--archive-base-url )
|
||||||
|
[ $# -lt 2 ] && { echo "pyenv-binary: --archive-base-url needs a value" >&2; exit 1; }
|
||||||
|
archive_base_url="$2"; shift ;;
|
||||||
|
-v|--verbose)
|
||||||
|
verbose=1 ;;
|
||||||
|
-* )
|
||||||
|
echo "pyenv-binary: unknown option \`$1'" >&2; exit 1 ;;
|
||||||
|
* )
|
||||||
|
[ -z "$spec" ] || { echo "pyenv-binary: unexpected argument \`$1'" >&2; exit 1; }
|
||||||
|
spec="$1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$spec" ] || [ -z "$archive_base_url" ]; then
|
||||||
|
pyenv-help --usage binary-package >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$spec" in
|
||||||
|
*?:?* ) entry="${spec##*:}" ;;
|
||||||
|
* ) entry="$(pyenv-binary-package-name "$spec")"; spec="${spec}:${entry}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$entry" in
|
||||||
|
# `pyenv install' reads a trailing `:latest' as part of the version rather than
|
||||||
|
# as an alias, so nothing would end up installed under that name.
|
||||||
|
latest )
|
||||||
|
echo "pyenv-binary: \`latest' cannot be used as an entry name" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
# The entry becomes a directory name under versions/. `pyenv install' does not
|
||||||
|
# validate the alias, and `save' only checks once the build is done, so refuse
|
||||||
|
# a name that could point elsewhere before compiling anything.
|
||||||
|
*/* | .. | . )
|
||||||
|
echo "pyenv-binary: invalid entry name \`${entry}'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# `pyenv install' puts a `<version>:<alias>' build under versions/<alias>.
|
||||||
|
pyenv-install ${verbose:+--verbose} "$spec"
|
||||||
|
pyenv-binary-save "$entry" "$PWD" --name "$entry"
|
||||||
|
|
||||||
|
pyenv-binary-generate-installer "${entry}.meta" \
|
||||||
|
--archive-url "${archive_base_url%/}/${entry}.tar.gz" -o "$entry"
|
||||||
73
plugins/pyenv-binary/libexec/pyenv-binary-package-name
Executable file
73
plugins/pyenv-binary/libexec/pyenv-binary-package-name
Executable file
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Generate a binary package name for the current platform
|
||||||
|
#
|
||||||
|
# Usage: pyenv binary package-name <version>
|
||||||
|
#
|
||||||
|
# Prints a package name containing the Python version, platform name,
|
||||||
|
# platform version and architecture.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
exec pyenv-install --list --bare
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]; then
|
||||||
|
pyenv-help --usage binary-package-name >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
version="$1"
|
||||||
|
|
||||||
|
case "$version" in
|
||||||
|
*/* | .. | . | *[[:cntrl:]]* )
|
||||||
|
echo "pyenv-binary: invalid version name \`${version}'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
version="$(pyenv-latest -f -k "$version")"
|
||||||
|
|
||||||
|
os="$(uname -s)"
|
||||||
|
arch="$(uname -m)"
|
||||||
|
distro=""
|
||||||
|
distro_version=""
|
||||||
|
|
||||||
|
case "$os" in
|
||||||
|
Linux )
|
||||||
|
if type -p lsb_release >/dev/null; then
|
||||||
|
distro="$(lsb_release -si)"
|
||||||
|
distro_version="$(lsb_release -sr)"
|
||||||
|
elif [ -r /etc/os-release ]; then
|
||||||
|
distro="$(. /etc/os-release && printf '%s' "${ID:-}")"
|
||||||
|
distro_version="$(. /etc/os-release && printf '%s' "${VERSION_ID:-}")"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
Darwin )
|
||||||
|
distro="macos"
|
||||||
|
distro_version="$(sw_vers -productVersion)"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
distro="$os"
|
||||||
|
distro_version="$(uname -r)"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
slug() {
|
||||||
|
printf '%s' "$1" |
|
||||||
|
tr '[:upper:] ' '[:lower:]-' |
|
||||||
|
tr -cd 'a-z0-9._-'
|
||||||
|
}
|
||||||
|
|
||||||
|
distro="$(slug "$distro")"
|
||||||
|
distro_version="$(slug "$distro_version")"
|
||||||
|
arch="$(slug "$arch")"
|
||||||
|
|
||||||
|
if [ -z "$distro" ] || [ -z "$distro_version" ] || [ -z "$arch" ]; then
|
||||||
|
echo "pyenv-binary: could not determine the package platform" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s-%s-%s-%s\n' "$version" "$distro" "$distro_version" "$arch"
|
||||||
137
plugins/pyenv-binary/libexec/pyenv-binary-relocate
Executable file
137
plugins/pyenv-binary/libexec/pyenv-binary-relocate
Executable file
|
|
@ -0,0 +1,137 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Rewrite an unpacked Python's library paths so it runs from its prefix
|
||||||
|
#
|
||||||
|
# Usage: pyenv binary relocate <prefix> [<build-prefix>]
|
||||||
|
#
|
||||||
|
# Rewrites the library paths of a Python tree that was unpacked into <prefix>
|
||||||
|
# so the interpreter and its extension modules load the bundled libraries from
|
||||||
|
# their new location rather than the path it was built at. Requires patchelf on
|
||||||
|
# Linux and FreeBSD, or otool and install_name_tool on macOS.
|
||||||
|
#
|
||||||
|
# The definition that `pyenv binary generate-installer' produces calls this
|
||||||
|
# after `install_package ... copy' has laid the tree down.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
prefix="$1"
|
||||||
|
if [ -z "$prefix" ]; then
|
||||||
|
pyenv-help --usage binary-relocate >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(uname -s)" = "Darwin" ]; then
|
||||||
|
build_prefix="${2%/}"
|
||||||
|
if [ -z "$build_prefix" ]; then
|
||||||
|
echo "pyenv-binary: need the original build prefix to relocate a macOS binary" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for tool in otool install_name_tool; do
|
||||||
|
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||||
|
echo "pyenv-binary: need ${tool} to relocate the binary" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
relocate_macho() {
|
||||||
|
local file="$1" new_rpath="$2"
|
||||||
|
local load_commands install_ids rpaths dependency install_id rpath replacement
|
||||||
|
|
||||||
|
load_commands="$(otool -L "$file")"
|
||||||
|
install_ids="$(otool -D "$file" 2>/dev/null || true)"
|
||||||
|
rpaths="$(otool -l "$file")"
|
||||||
|
install_id="$(printf '%s\n' "$install_ids" | sed -n '2s/^[[:space:]]*//p')"
|
||||||
|
|
||||||
|
while IFS= read -r dependency; do
|
||||||
|
case "$dependency" in
|
||||||
|
"$build_prefix"/lib/* )
|
||||||
|
if [ "$dependency" != "$install_id" ]; then
|
||||||
|
replacement="@rpath/${dependency#"$build_prefix"/lib/}"
|
||||||
|
install_name_tool -change "$dependency" "$replacement" "$file"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(printf '%s\n' "$load_commands" | tail -n +2 | sed -E \
|
||||||
|
's/^[[:space:]]*//; s/[[:space:]]+\(compatibility version.*$//')
|
||||||
|
|
||||||
|
case "$install_id" in
|
||||||
|
"$build_prefix"/lib/* )
|
||||||
|
replacement="@rpath/${install_id#"$build_prefix"/lib/}"
|
||||||
|
install_name_tool -id "$replacement" "$file"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
while IFS= read -r rpath; do
|
||||||
|
if [ "$rpath" = "$build_prefix/lib" ]; then
|
||||||
|
install_name_tool -rpath "$rpath" "$new_rpath" "$file"
|
||||||
|
fi
|
||||||
|
done < <(printf '%s\n' "$rpaths" | awk '
|
||||||
|
/cmd LC_RPATH/ { found = 1; next }
|
||||||
|
found && /^[[:space:]]*path / {
|
||||||
|
sub(/^[[:space:]]*path /, "")
|
||||||
|
sub(/ \(offset [0-9]+\)$/, "")
|
||||||
|
print
|
||||||
|
found = 0
|
||||||
|
}
|
||||||
|
')
|
||||||
|
}
|
||||||
|
|
||||||
|
found=0
|
||||||
|
for file in "$prefix"/bin/*; do
|
||||||
|
[[ -f $file && -x $file && ! -L $file ]] || continue
|
||||||
|
otool -L "$file" >/dev/null 2>&1 || continue
|
||||||
|
relocate_macho "$file" '@executable_path/../lib'
|
||||||
|
found=1
|
||||||
|
done
|
||||||
|
if [ "$found" -eq 0 ]; then
|
||||||
|
echo "pyenv-binary: found no interpreter to relocate under \`${prefix}/bin'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for file in "$prefix"/lib/*.dylib; do
|
||||||
|
[[ -f $file && ! -L $file ]] || continue
|
||||||
|
relocate_macho "$file" '@loader_path'
|
||||||
|
done
|
||||||
|
while IFS= read -r so; do
|
||||||
|
relocate_macho "$so" '@loader_path/../..'
|
||||||
|
done < <(find "$prefix" -type f -path '*/lib/python*/lib-dynload/*.so')
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
# patchelf can add an rpath and write one of any length; chrpath can only shorten
|
||||||
|
# an existing one, which is not enough to relocate every build, so require it.
|
||||||
|
if ! command -v patchelf >/dev/null 2>&1; then
|
||||||
|
echo "pyenv-binary: need patchelf to relocate the binary" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The interpreter loads libpython from ../lib. Patch every ELF binary in bin/ so
|
||||||
|
# this holds whatever the interpreter is named (python3, python2.7, ...); the
|
||||||
|
# scripts alongside it (pip, idle) are not ELF, so `--print-rpath' fails on them
|
||||||
|
# and they are skipped. A tree with no interpreter at all did not unpack the way
|
||||||
|
# we expect, so treat that as an error rather than quietly relocating nothing.
|
||||||
|
found=0
|
||||||
|
for file in "$prefix"/bin/*; do
|
||||||
|
[[ -f $file && -x $file && ! -L $file ]] || continue
|
||||||
|
patchelf --print-rpath "$file" >/dev/null 2>&1 || continue
|
||||||
|
patchelf --set-rpath "$prefix/lib" "$file"
|
||||||
|
found=1
|
||||||
|
done
|
||||||
|
if [ "$found" -eq 0 ]; then
|
||||||
|
echo "pyenv-binary: found no interpreter to relocate under \`${prefix}/bin'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The extension modules CPython built are the only other objects with an rpath
|
||||||
|
# into the old prefix, so point them at lib/ as well. Anything a wheel installed
|
||||||
|
# carries an rpath of its own, often into a bundled library directory beside it,
|
||||||
|
# and would stop loading if we overwrote it. Each match is an ELF object we expect
|
||||||
|
# to patch, so let a failure stop us rather than swallow it.
|
||||||
|
while IFS= read -r so; do
|
||||||
|
patchelf --set-rpath "$prefix/lib" "$so"
|
||||||
|
done < <(find "$prefix" -path '*/lib-dynload/*.so')
|
||||||
157
plugins/pyenv-binary/libexec/pyenv-binary-save
Executable file
157
plugins/pyenv-binary/libexec/pyenv-binary-save
Executable file
|
|
@ -0,0 +1,157 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Save an installed Python version as a relocatable archive
|
||||||
|
#
|
||||||
|
# Usage: pyenv binary save <version> [<output-dir>] [--name <name>]
|
||||||
|
#
|
||||||
|
# Packs an installed version into a relocatable .tar.gz (relative paths) and
|
||||||
|
# writes a metadata file listing the build platform and the system libraries
|
||||||
|
# it links against, so an installer can check compatibility before unpacking.
|
||||||
|
#
|
||||||
|
# <version> An installed version, as listed by `pyenv versions --bare'.
|
||||||
|
# <output-dir> Where to write the archive and metadata (default: `.').
|
||||||
|
# --name <name> Use <name> as the archive and metadata base name instead of
|
||||||
|
# <version>-<platform>.
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
# Provide pyenv completions
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
echo --name
|
||||||
|
exec pyenv-versions --bare
|
||||||
|
fi
|
||||||
|
|
||||||
|
version=""
|
||||||
|
output_dir=""
|
||||||
|
package_name=""
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--name )
|
||||||
|
[ $# -ge 2 ] && [ -n "$2" ] || { echo "pyenv-binary: --name needs a value" >&2; exit 1; }
|
||||||
|
package_name="$2"; shift 2 ;;
|
||||||
|
-* )
|
||||||
|
echo "pyenv-binary: unknown option \`$1'" >&2; exit 1 ;;
|
||||||
|
* )
|
||||||
|
if [ -z "$version" ]; then
|
||||||
|
version="$1"
|
||||||
|
elif [ -z "$output_dir" ]; then
|
||||||
|
output_dir="$1"
|
||||||
|
else
|
||||||
|
echo "pyenv-binary: unexpected argument \`$1'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
output_dir="${output_dir:-$PWD}"
|
||||||
|
|
||||||
|
if [ -z "$version" ]; then
|
||||||
|
echo "Usage: pyenv binary save <version> [<output-dir>] [--name <name>]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A version is a single directory name under versions/. With no slash allowed,
|
||||||
|
# the only remaining names that could point elsewhere are `.' and `..'.
|
||||||
|
case "$version" in
|
||||||
|
*/* | .. | . )
|
||||||
|
echo "pyenv-binary: invalid version name \`${version}'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$package_name" in
|
||||||
|
*/* | .. | . | *[[:cntrl:]]* )
|
||||||
|
echo "pyenv-binary: invalid package name \`${package_name}'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
prefix="${PYENV_ROOT}/versions/${version}"
|
||||||
|
if [ ! -d "${prefix}/bin" ]; then
|
||||||
|
echo "pyenv-binary: version \`${version}' is not installed" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
os="$(uname -s)"
|
||||||
|
arch="$(uname -m)"
|
||||||
|
platform="$(printf '%s' "$os" | tr '[:upper:]' '[:lower:]')-${arch}"
|
||||||
|
|
||||||
|
# Record the distro and libc version. Platform and arch alone are too coarse to
|
||||||
|
# judge compatibility: a build is only portable to a matching libc (e.g. a
|
||||||
|
# glibc 2.36 build will not load on an older glibc, nor on musl at all).
|
||||||
|
distro=""
|
||||||
|
libc=""
|
||||||
|
if [ -r /etc/os-release ]; then
|
||||||
|
distro="$( . /etc/os-release && printf '%s %s' "${ID:-}" "${VERSION_ID:-}" )"
|
||||||
|
elif [ "$os" = "Darwin" ]; then
|
||||||
|
distro="macos $(sw_vers -productVersion 2>/dev/null)"
|
||||||
|
fi
|
||||||
|
if [ "$os" = "Linux" ]; then
|
||||||
|
libc="$(getconf GNU_LIBC_VERSION 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
if [ "$os" != "Darwin" ] && ! LC_ALL=C readelf --version &>/dev/null; then
|
||||||
|
echo "pyenv-binary: need readelf to inspect shared libraries" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# List the external shared libraries the install links against: those that
|
||||||
|
# resolve outside its own prefix, so they must already exist on the target.
|
||||||
|
# The interpreter plus every bundled shared object are inspected.
|
||||||
|
system_deps() {
|
||||||
|
local f needed
|
||||||
|
{
|
||||||
|
for f in "${prefix}"/bin/python*; do
|
||||||
|
[ -e "$f" ] && printf '%s\n' "$f"
|
||||||
|
done
|
||||||
|
# CPython ships its extension modules as *.so (and *.dylib on macOS). A
|
||||||
|
# bare *.so.* is unusual for CPython itself, but the odd build carries a
|
||||||
|
# versioned copy alongside, so match it too rather than miss a dependency.
|
||||||
|
find "${prefix}" -type f \( -name '*.so' -o -name '*.so.*' -o -name '*.dylib' \)
|
||||||
|
} | sort -u | while IFS= read -r f; do
|
||||||
|
if [ "$os" = "Darwin" ]; then
|
||||||
|
otool -L "$f" 2>/dev/null | tail -n +2 | awk -v pfx="${prefix}/" \
|
||||||
|
'$1 !~ /^@/ && substr($1, 1, length(pfx)) != pfx { print $1 }'
|
||||||
|
else
|
||||||
|
# GNU binutils 2.44 writes "(NEEDED) ... [name]"; FreeBSD 15.1 writes
|
||||||
|
# "NEEDED ... [name]". readelf(1) documents -d, not its text format.
|
||||||
|
needed="$(LC_ALL=C readelf -dW "$f" 2>/dev/null | awk \
|
||||||
|
'$2 == "(NEEDED)" || $2 == "NEEDED" { sub(/^.*\[/, ""); sub(/\].*$/, ""); print }' | tr '\n' ' ')"
|
||||||
|
# Linux and FreeBSD ldd write resolved libraries as
|
||||||
|
# "name => /absolute/path (address)"; entries without "=>" are ignored.
|
||||||
|
LC_ALL=C ldd "$f" 2>/dev/null | awk -v needed="$needed" -v pfx="${prefix}/" '
|
||||||
|
BEGIN {
|
||||||
|
split(needed, deps, " ")
|
||||||
|
for (i in deps) direct[deps[i]] = 1
|
||||||
|
}
|
||||||
|
$1 in direct && $2 == "=>" && $3 ~ /^\// && substr($3, 1, length(pfx)) != pfx { print $1 }
|
||||||
|
'
|
||||||
|
fi
|
||||||
|
done | sort -u
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "$output_dir"
|
||||||
|
package_name="${package_name:-${version}-${platform}}"
|
||||||
|
archive="${package_name}.tar.gz"
|
||||||
|
metadata="${package_name}.meta"
|
||||||
|
|
||||||
|
tar -C "$(dirname "$prefix")" -czf "${output_dir}/${archive}" "$(basename "$prefix")"
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "# pyenv-binary metadata"
|
||||||
|
echo "version=${version}"
|
||||||
|
echo "os=${os}"
|
||||||
|
echo "arch=${arch}"
|
||||||
|
echo "platform=${platform}"
|
||||||
|
[ -n "$distro" ] && echo "distro=${distro}"
|
||||||
|
[ -n "$libc" ] && echo "libc=${libc}"
|
||||||
|
echo "build_prefix=${prefix}"
|
||||||
|
echo "archive=${archive}"
|
||||||
|
system_deps | while IFS= read -r dep; do
|
||||||
|
[ -n "$dep" ] && echo "dep=${dep}"
|
||||||
|
done
|
||||||
|
} > "${output_dir}/${metadata}"
|
||||||
|
|
||||||
|
echo "Saved ${archive} and ${metadata} to ${output_dir}"
|
||||||
212
plugins/pyenv-binary/test/generate-installer.bats
Normal file
212
plugins/pyenv-binary/test/generate-installer.bats
Normal file
|
|
@ -0,0 +1,212 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load test_helper
|
||||||
|
|
||||||
|
create_meta() {
|
||||||
|
local os="${1-Linux}"
|
||||||
|
local arch="${2-x86_64}"
|
||||||
|
local libc="${3-glibc 2.17}"
|
||||||
|
local build_prefix="${4-}"
|
||||||
|
local archive="${BATS_TEST_TMPDIR}/3.12.7.tar.gz"
|
||||||
|
local meta="${BATS_TEST_TMPDIR}/sample.meta"
|
||||||
|
|
||||||
|
rm -rf "${BATS_TEST_TMPDIR}/archive"
|
||||||
|
mkdir -p "${BATS_TEST_TMPDIR}/archive/3.12.7/bin"
|
||||||
|
printf '#!/bin/sh\n' > "${BATS_TEST_TMPDIR}/archive/3.12.7/bin/python"
|
||||||
|
chmod +x "${BATS_TEST_TMPDIR}/archive/3.12.7/bin/python"
|
||||||
|
tar -C "${BATS_TEST_TMPDIR}/archive" -czf "$archive" 3.12.7
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "version=3.12.7"
|
||||||
|
echo "os=${os}"
|
||||||
|
echo "arch=${arch}"
|
||||||
|
[ -z "$libc" ] || echo "libc=${libc}"
|
||||||
|
[ -z "$build_prefix" ] || echo "build_prefix=${build_prefix}"
|
||||||
|
echo "archive=${archive##*/}"
|
||||||
|
} > "$meta"
|
||||||
|
echo "$meta"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "completion lists the options" {
|
||||||
|
run pyenv-binary-generate-installer --complete
|
||||||
|
assert_success "--archive-url
|
||||||
|
-o"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails with no arguments" {
|
||||||
|
create_stub pyenv-help "echo usage"
|
||||||
|
run pyenv-binary-generate-installer
|
||||||
|
assert_failure "usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails without an archive url" {
|
||||||
|
create_stub pyenv-help "echo usage"
|
||||||
|
run pyenv-binary-generate-installer "$(create_meta)"
|
||||||
|
assert_failure "usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when --archive-url has no value" {
|
||||||
|
run pyenv-binary-generate-installer "$(create_meta)" --archive-url
|
||||||
|
assert_failure "pyenv-binary: --archive-url needs a value"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects a second positional argument" {
|
||||||
|
run pyenv-binary-generate-installer "$(create_meta)" extra --archive-url http://x/a.tar.gz
|
||||||
|
assert_failure "pyenv-binary: unexpected argument \`extra'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails for a metadata file that cannot be read" {
|
||||||
|
run pyenv-binary-generate-installer /no/such.meta --archive-url http://x/a.tar.gz
|
||||||
|
assert_failure
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when macOS metadata is missing the build prefix" {
|
||||||
|
run pyenv-binary-generate-installer "$(create_meta Darwin arm64 '')" \
|
||||||
|
--archive-url http://x/a.tar.gz
|
||||||
|
assert_failure "pyenv-binary: metadata is missing \`build_prefix'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "quotes the macOS build prefix in the generated definition" {
|
||||||
|
local out="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
pyenv-binary-generate-installer \
|
||||||
|
"$(create_meta Darwin arm64 '' '/build prefix/$name')" \
|
||||||
|
--archive-url http://example.com/a.tar.gz -o "$out"
|
||||||
|
|
||||||
|
run grep '^BUILD_PREFIX=' "$out"
|
||||||
|
assert_success 'BUILD_PREFIX=/build\ prefix/\$name'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when required metadata is missing" {
|
||||||
|
local field meta
|
||||||
|
for field in version os arch archive; do
|
||||||
|
meta="$(create_meta)"
|
||||||
|
#cannot use -i: GNU sed requires -i[suf], BSD sed required -i <suf>
|
||||||
|
sed "/^${field}=/d" "$meta" > "${meta}.tmp"; mv "${meta}"{.tmp,}
|
||||||
|
|
||||||
|
run pyenv-binary-generate-installer "$meta" --archive-url http://x/a.tar.gz
|
||||||
|
assert_failure "pyenv-binary: metadata is missing \`${field}'"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when Linux metadata is missing libc" {
|
||||||
|
run pyenv-binary-generate-installer "$(create_meta Linux x86_64 '')" \
|
||||||
|
--archive-url http://x/a.tar.gz
|
||||||
|
assert_failure "pyenv-binary: metadata is missing \`libc'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "refuses a host whose glibc is older than the archive" {
|
||||||
|
local out="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
pyenv-binary-generate-installer "$(create_meta Linux x86_64 'glibc 99.0')" \
|
||||||
|
--archive-url http://example.com/a.tar.gz -o "$out"
|
||||||
|
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
|
||||||
|
create_stub getconf 'echo "glibc 2.31"'
|
||||||
|
|
||||||
|
run bash "$out"
|
||||||
|
assert_failure "pyenv-binary: archive needs glibc 99.0 or newer, but this system has 2.31"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "checks required libraries in the FreeBSD ldconfig cache" {
|
||||||
|
local out="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
local meta="$(create_meta FreeBSD amd64 '')"
|
||||||
|
printf 'dep=libc.so.7\ndep=libmissing.so.1\n' >> "$meta"
|
||||||
|
pyenv-binary-generate-installer "$meta" \
|
||||||
|
--archive-url http://example.com/a.tar.gz -o "$out"
|
||||||
|
create_stub uname 'case "$1" in -s) echo FreeBSD;; -m) echo amd64;; esac'
|
||||||
|
create_stub ldconfig '[ "$1" = "-r" ] && echo "0:-lc.7=>/lib/libc.so.7"'
|
||||||
|
|
||||||
|
run bash "$out"
|
||||||
|
assert_failure "pyenv-binary: missing required system libraries: libmissing.so.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "checks for patchelf before installing the archive" {
|
||||||
|
local out="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
pyenv-binary-generate-installer "$(create_meta)" \
|
||||||
|
--archive-url http://example.com/a.tar.gz -o "$out"
|
||||||
|
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
|
||||||
|
create_stub getconf 'echo "glibc 2.31"'
|
||||||
|
|
||||||
|
PATH="$(path_without patchelf)" run bash "$out"
|
||||||
|
assert_failure "pyenv-binary: need patchelf to relocate the binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "checks for otool before installing a macOS archive" {
|
||||||
|
local out="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
pyenv-binary-generate-installer \
|
||||||
|
"$(create_meta Darwin arm64 '' /build/3.12.7)" \
|
||||||
|
--archive-url http://example.com/a.tar.gz -o "$out"
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_path_executable install_name_tool true
|
||||||
|
|
||||||
|
PATH="$(path_without otool)" run bash "$out"
|
||||||
|
assert_failure "pyenv-binary: need otool to relocate the binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "checks for install_name_tool before installing a macOS archive" {
|
||||||
|
local out="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
pyenv-binary-generate-installer \
|
||||||
|
"$(create_meta Darwin arm64 '' /build/3.12.7)" \
|
||||||
|
--archive-url http://example.com/a.tar.gz -o "$out"
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_path_executable otool true
|
||||||
|
|
||||||
|
PATH="$(path_without install_name_tool)" run bash "$out"
|
||||||
|
assert_failure "pyenv-binary: need install_name_tool to relocate the binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "the generated macOS definition preserves both relocation arguments" {
|
||||||
|
local archive="${BATS_TEST_TMPDIR}/3.12.7.tar.gz"
|
||||||
|
local cache="${BATS_TEST_TMPDIR}/cache"
|
||||||
|
local definition="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
local prefix="${BATS_TEST_TMPDIR}/install"
|
||||||
|
pyenv-binary-generate-installer \
|
||||||
|
"$(create_meta Darwin arm64 '' '/build prefix/$name')" \
|
||||||
|
--archive-url http://example.com/3.12.7.tar.gz -o "$definition"
|
||||||
|
mkdir -p "$cache"
|
||||||
|
cp "$archive" "${cache}/Python-3.12.7-binary.tar.gz"
|
||||||
|
create_stub pyenv 'printf "argc=%s\narg1=%s\narg2=%s\narg3=%s\narg4=%s\n" "$#" "$1" "$2" "$3" "$4"'
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_stub sw_vers 'echo 15.5'
|
||||||
|
create_stub ldconfig true
|
||||||
|
create_path_executable otool true
|
||||||
|
create_path_executable install_name_tool true
|
||||||
|
|
||||||
|
PYTHON_BUILD_CACHE_PATH="$cache" run \
|
||||||
|
"${BATS_TEST_DIRNAME}/../../python-build/bin/python-build" "$definition" "$prefix"
|
||||||
|
assert_success
|
||||||
|
assert_line "argc=4"
|
||||||
|
assert_line "arg1=binary"
|
||||||
|
assert_line "arg2=relocate"
|
||||||
|
assert_line "arg3=${prefix}"
|
||||||
|
assert_line 'arg4=/build prefix/$name'
|
||||||
|
assert_line "Installed Python-3.12.7-binary to ${prefix}"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when the archive is not beside the metadata" {
|
||||||
|
local meta="$(create_meta)"
|
||||||
|
rm "${BATS_TEST_TMPDIR}/3.12.7.tar.gz"
|
||||||
|
|
||||||
|
run pyenv-binary-generate-installer "$meta" --archive-url http://x/a.tar.gz
|
||||||
|
assert_failure "pyenv-binary: cannot read the archive \`${BATS_TEST_TMPDIR}/3.12.7.tar.gz' to checksum it"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "the generated definition is installable with python-build" {
|
||||||
|
ldconfig -p &>/dev/null || skip "ldconfig with -p is not present"
|
||||||
|
local archive="${BATS_TEST_TMPDIR}/3.12.7.tar.gz"
|
||||||
|
local cache="${BATS_TEST_TMPDIR}/cache"
|
||||||
|
local definition="${BATS_TEST_TMPDIR}/definition"
|
||||||
|
local prefix="${BATS_TEST_TMPDIR}/install"
|
||||||
|
pyenv-binary-generate-installer "$(create_meta)" \
|
||||||
|
--archive-url http://example.com/3.12.7.tar.gz -o "$definition"
|
||||||
|
mkdir -p "$cache"
|
||||||
|
cp "$archive" "${cache}/Python-3.12.7-binary.tar.gz"
|
||||||
|
create_stub pyenv 'echo "pyenv $*"'
|
||||||
|
create_path_executable patchelf "exit 0"
|
||||||
|
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
|
||||||
|
|
||||||
|
PYTHON_BUILD_CACHE_PATH="$cache" run \
|
||||||
|
"${BATS_TEST_DIRNAME}/../../python-build/bin/python-build" "$definition" "$prefix"
|
||||||
|
assert_success
|
||||||
|
assert_line "pyenv binary relocate ${prefix}"
|
||||||
|
assert_line "Installed Python-3.12.7-binary to ${prefix}"
|
||||||
|
assert [ -x "${prefix}/bin/python" ]
|
||||||
|
}
|
||||||
59
plugins/pyenv-binary/test/package-name.bats
Normal file
59
plugins/pyenv-binary/test/package-name.bats
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load test_helper
|
||||||
|
|
||||||
|
_setup() {
|
||||||
|
create_stub pyenv-latest '[ "$1" = "-f" ] && [ "$2" = "-k" ] && shift 2 && echo "$*"'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "completion lists installable versions" {
|
||||||
|
create_stub pyenv-install \
|
||||||
|
'[ "$*" = "--list --bare" ] && echo 3.13.14'
|
||||||
|
|
||||||
|
run pyenv-binary-package-name --complete
|
||||||
|
assert_success "3.13.14"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails without a version" {
|
||||||
|
create_stub pyenv-help 'echo usage'
|
||||||
|
|
||||||
|
run pyenv-binary-package-name
|
||||||
|
assert_failure "usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects a second argument" {
|
||||||
|
create_stub pyenv-help 'echo usage'
|
||||||
|
|
||||||
|
run pyenv-binary-package-name 3.13.14 extra
|
||||||
|
assert_failure "usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generates a package name for Linux" {
|
||||||
|
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
|
||||||
|
create_stub lsb_release 'case "$1" in -si) echo Debian;; -sr) echo 12;; esac'
|
||||||
|
|
||||||
|
run pyenv-binary-package-name 3.13.14
|
||||||
|
assert_success "3.13.14-debian-12-x86_64"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generates a package name for macOS" {
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_stub sw_vers 'echo 15.5'
|
||||||
|
|
||||||
|
run pyenv-binary-package-name 3.13.14
|
||||||
|
assert_success "3.13.14-macos-15.5-arm64"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "resolves a version prefix when generating a package name" {
|
||||||
|
create_stub pyenv-latest '[ "$*" = "-f -k 3" ] && echo 3.14.7'
|
||||||
|
create_stub uname \
|
||||||
|
'case "$1" in -s) echo FreeBSD;; -m) echo amd64;; -r) echo 14.2-RELEASE-p3;; esac'
|
||||||
|
|
||||||
|
run pyenv-binary-package-name 3
|
||||||
|
assert_success "3.14.7-freebsd-14.2-release-p3-amd64"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects an invalid version name" {
|
||||||
|
run pyenv-binary-package-name ../3.13.14
|
||||||
|
assert_failure "pyenv-binary: invalid version name \`../3.13.14'"
|
||||||
|
}
|
||||||
131
plugins/pyenv-binary/test/package.bats
Normal file
131
plugins/pyenv-binary/test/package.bats
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load test_helper
|
||||||
|
|
||||||
|
# Make the build deterministic: `pyenv-install' just creates the prefix, and
|
||||||
|
# the platform tools report a fixed Linux target so the real `save' and
|
||||||
|
# `generate-installer' behave the same on any test host.
|
||||||
|
stub_build_environment() {
|
||||||
|
create_stub pyenv-install 'echo "${0##*/} $*"; mkdir -p "${PYENV_ROOT}/versions/${1##*:}/bin"'
|
||||||
|
create_stub pyenv-latest 'while (($#)); do case "$1" in -f|-k);; *)break;; esac; shift; done; echo "$*"'
|
||||||
|
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
|
||||||
|
create_stub getconf 'echo "glibc 2.17"'
|
||||||
|
create_stub readelf true
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "-v|--verbose runs pyenv install verbosely" {
|
||||||
|
stub_build_environment
|
||||||
|
create_stub pyenv-binary-save true
|
||||||
|
create_stub pyenv-binary-generate-installer true
|
||||||
|
|
||||||
|
for opt in "" -v --verbose; do
|
||||||
|
run pyenv-binary-package $opt 3.12.7:3.12.7-test \
|
||||||
|
--archive-base-url http://example.com/binaries
|
||||||
|
assert_success "pyenv-install ${opt:+--verbose }3.12.7:3.12.7-test"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "completions" {
|
||||||
|
create_stub pyenv-install 'echo "${0##*/} $*"'
|
||||||
|
|
||||||
|
run pyenv-binary-package --complete
|
||||||
|
assert_success <<!
|
||||||
|
--archive-base-url
|
||||||
|
--verbose
|
||||||
|
pyenv-install --list --bare
|
||||||
|
!
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails with no arguments" {
|
||||||
|
create_stub pyenv-help "echo usage"
|
||||||
|
run pyenv-binary-package
|
||||||
|
assert_failure "usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails without an archive base url" {
|
||||||
|
create_stub pyenv-help "echo usage"
|
||||||
|
run pyenv-binary-package 3.12.7:3.12.7-test
|
||||||
|
assert_failure "usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when --archive-base-url has no value" {
|
||||||
|
run pyenv-binary-package 3.12.7:3.12.7-test --archive-base-url
|
||||||
|
assert_failure "pyenv-binary: --archive-base-url needs a value"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects a second positional argument" {
|
||||||
|
run pyenv-binary-package 3.12.7:3.12.7-test extra --archive-base-url http://x/b
|
||||||
|
assert_failure "pyenv-binary: unexpected argument \`extra'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "generates an entry name for a bare version" {
|
||||||
|
stub_build_environment
|
||||||
|
create_stub lsb_release 'case "$1" in -si) echo Debian;; -sr) echo 12;; esac'
|
||||||
|
cd "${BATS_TEST_TMPDIR}"
|
||||||
|
|
||||||
|
run pyenv-binary-package 3.12.7 --archive-base-url http://example.com/binaries
|
||||||
|
assert_success
|
||||||
|
assert [ -d "${PYENV_ROOT}/versions/3.12.7-debian-12-x86_64" ]
|
||||||
|
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-debian-12-x86_64.tar.gz" ]
|
||||||
|
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-debian-12-x86_64.meta" ]
|
||||||
|
run grep '^ARCHIVE_URL=' "${BATS_TEST_TMPDIR}/3.12.7-debian-12-x86_64"
|
||||||
|
assert_success "ARCHIVE_URL=http://example.com/binaries/3.12.7-debian-12-x86_64.tar.gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects an entry name containing a slash" {
|
||||||
|
run pyenv-binary-package "3.12.7:foo/bar" --archive-base-url http://x/b
|
||||||
|
assert_failure "pyenv-binary: invalid entry name \`foo/bar'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects \`latest' as an entry name" {
|
||||||
|
run pyenv-binary-package 3.12:latest --archive-base-url http://x/b
|
||||||
|
assert_failure "pyenv-binary: \`latest' cannot be used as an entry name"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "packages on macOS" {
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_stub pyenv-install 'echo install'
|
||||||
|
create_stub pyenv-binary-save 'echo save'
|
||||||
|
create_stub pyenv-binary-generate-installer 'echo generate-installer'
|
||||||
|
|
||||||
|
run pyenv-binary-package 3.12.7:3.12.7-test --archive-base-url http://x/b
|
||||||
|
assert_success "install
|
||||||
|
save
|
||||||
|
generate-installer"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "writes the archive, metadata and definition under the entry name (integration)" {
|
||||||
|
stub_build_environment
|
||||||
|
cd "${BATS_TEST_TMPDIR}"
|
||||||
|
|
||||||
|
run pyenv-binary-package 3.12.7:3.12.7-test \
|
||||||
|
--archive-base-url http://example.com/binaries
|
||||||
|
assert_success
|
||||||
|
assert [ -d "${PYENV_ROOT}/versions/3.12.7-test" ]
|
||||||
|
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-test.tar.gz" ]
|
||||||
|
assert [ -f "${BATS_TEST_TMPDIR}/3.12.7-test.meta" ]
|
||||||
|
run grep '^ARCHIVE_URL=' "${BATS_TEST_TMPDIR}/3.12.7-test"
|
||||||
|
assert_success "ARCHIVE_URL=http://example.com/binaries/3.12.7-test.tar.gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "correctly joins archive base url with a trailing slash" {
|
||||||
|
stub_build_environment
|
||||||
|
create_stub pyenv-binary-save true
|
||||||
|
create_stub pyenv-binary-generate-installer <<'!'
|
||||||
|
echo -n "${0##*/} "
|
||||||
|
while (($#)); do
|
||||||
|
case $1 in
|
||||||
|
--archive-url)
|
||||||
|
echo "$1 ${2:?}"
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
!
|
||||||
|
|
||||||
|
run pyenv-binary-package 3.12.7:3.12.7-test \
|
||||||
|
--archive-base-url http://example.com/binaries/
|
||||||
|
assert_success
|
||||||
|
assert_line "pyenv-binary-generate-installer --archive-url http://example.com/binaries/3.12.7-test.tar.gz"
|
||||||
|
}
|
||||||
221
plugins/pyenv-binary/test/relocate.bats
Normal file
221
plugins/pyenv-binary/test/relocate.bats
Normal file
|
|
@ -0,0 +1,221 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load test_helper
|
||||||
|
|
||||||
|
_setup() {
|
||||||
|
create_stub pyenv-help "echo usage"
|
||||||
|
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
|
||||||
|
}
|
||||||
|
|
||||||
|
stub_patchelf() {
|
||||||
|
create_path_executable patchelf <<STUB
|
||||||
|
if [ "\$1" = "--print-rpath" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "\$*" >> "${BATS_TEST_TMPDIR}/patchelf.log"
|
||||||
|
STUB
|
||||||
|
}
|
||||||
|
|
||||||
|
create_interpreter() {
|
||||||
|
mkdir -p "${BATS_TEST_TMPDIR}/prefix/bin"
|
||||||
|
printf '#!/bin/sh\n' > "${BATS_TEST_TMPDIR}/prefix/bin/python2.7"
|
||||||
|
chmod +x "${BATS_TEST_TMPDIR}/prefix/bin/python2.7"
|
||||||
|
}
|
||||||
|
|
||||||
|
create_macos_tree() {
|
||||||
|
local prefix="${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
local lib="${prefix}/lib/python3.12"
|
||||||
|
mkdir -p "${prefix}/bin" "${lib}/lib-dynload" "${lib}/site-packages/numpy"
|
||||||
|
printf '#!/bin/sh\n' > "${prefix}/bin/python3.12"
|
||||||
|
chmod +x "${prefix}/bin/python3.12"
|
||||||
|
touch "${prefix}/lib/libpython3.12.dylib"
|
||||||
|
touch "${lib}/lib-dynload/_ssl.cpython-312-darwin.so"
|
||||||
|
touch "${lib}/site-packages/numpy/_multiarray.so"
|
||||||
|
}
|
||||||
|
|
||||||
|
stub_macos_tools() {
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_path_executable install_name_tool \
|
||||||
|
'echo "$*" >> "${BATS_TEST_TMPDIR}/install-name-tool.log"'
|
||||||
|
create_path_executable otool <<'STUB'
|
||||||
|
file="${!#}"
|
||||||
|
echo "$*" >> "${BATS_TEST_TMPDIR}/otool.log"
|
||||||
|
old="/build prefix/3.12.7"
|
||||||
|
case "$1" in
|
||||||
|
-L )
|
||||||
|
echo "${file}:"
|
||||||
|
case "$file" in
|
||||||
|
*/bin/python3.12 )
|
||||||
|
if [ -n "$OTOOL_RELOCATED" ]; then
|
||||||
|
echo " @rpath/libpython3.12.dylib (compatibility version 3.12.0, current version 3.12.0)"
|
||||||
|
else
|
||||||
|
echo " ${old}/lib/libpython3.12.dylib (compatibility version 3.12.0, current version 3.12.0)"
|
||||||
|
fi
|
||||||
|
echo " ${old}-other/lib/libother.dylib (compatibility version 1.0.0, current version 1.0.0)"
|
||||||
|
echo " /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1.0.0)"
|
||||||
|
echo " /opt/homebrew/lib/libintl.8.dylib (compatibility version 1.0.0, current version 1.0.0)"
|
||||||
|
echo " @loader_path/liblocal.dylib (compatibility version 1.0.0, current version 1.0.0)"
|
||||||
|
;;
|
||||||
|
*/lib/libpython3.12.dylib )
|
||||||
|
if [ -n "$OTOOL_RELOCATED" ]; then
|
||||||
|
echo " @rpath/libpython3.12.dylib (compatibility version 3.12.0, current version 3.12.0)"
|
||||||
|
else
|
||||||
|
echo " ${old}/lib/libpython3.12.dylib (compatibility version 3.12.0, current version 3.12.0)"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
-D )
|
||||||
|
case "$file" in
|
||||||
|
*/lib/libpython3.12.dylib )
|
||||||
|
echo "${file}:"
|
||||||
|
if [ -n "$OTOOL_RELOCATED" ]; then
|
||||||
|
echo '@rpath/libpython3.12.dylib'
|
||||||
|
else
|
||||||
|
echo "${old}/lib/libpython3.12.dylib"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
-l )
|
||||||
|
if [ -n "$OTOOL_RELOCATED" ]; then
|
||||||
|
case "$file" in
|
||||||
|
*/bin/* ) rpath='@executable_path/../lib' ;;
|
||||||
|
*/lib/*.dylib ) rpath='@loader_path' ;;
|
||||||
|
* ) rpath='@loader_path/../..' ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
rpath="${old}/lib"
|
||||||
|
fi
|
||||||
|
cat <<EOF
|
||||||
|
cmd LC_RPATH
|
||||||
|
path ${rpath} (offset 12)
|
||||||
|
cmd LC_RPATH
|
||||||
|
path /opt/homebrew/lib (offset 12)
|
||||||
|
cmd LC_RPATH
|
||||||
|
path @loader_path/vendor (offset 12)
|
||||||
|
EOF
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
STUB
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "completion produces nothing" {
|
||||||
|
run pyenv-binary-relocate --complete
|
||||||
|
assert_success ""
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails without a prefix" {
|
||||||
|
run pyenv-binary-relocate
|
||||||
|
assert_failure "usage"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when patchelf is not available" {
|
||||||
|
PATH="$(path_without patchelf)" run pyenv-binary-relocate "${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
assert_failure "pyenv-binary: need patchelf to relocate the binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails without the original build prefix on macOS" {
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
|
||||||
|
run pyenv-binary-relocate "${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
assert_failure "pyenv-binary: need the original build prefix to relocate a macOS binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when otool is not available on macOS" {
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_path_executable install_name_tool true
|
||||||
|
|
||||||
|
PATH="$(path_without otool)" run \
|
||||||
|
pyenv-binary-relocate "${BATS_TEST_TMPDIR}/prefix" /build/3.12.7
|
||||||
|
assert_failure "pyenv-binary: need otool to relocate the binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when install_name_tool is not available on macOS" {
|
||||||
|
create_stub uname 'case "$1" in -s) echo Darwin;; -m) echo arm64;; esac'
|
||||||
|
create_path_executable otool true
|
||||||
|
|
||||||
|
PATH="$(path_without install_name_tool)" run \
|
||||||
|
pyenv-binary-relocate "${BATS_TEST_TMPDIR}/prefix" /build/3.12.7
|
||||||
|
assert_failure "pyenv-binary: need install_name_tool to relocate the binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when the prefix has no interpreter" {
|
||||||
|
create_path_executable patchelf "exit 0"
|
||||||
|
mkdir -p "${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
|
||||||
|
run pyenv-binary-relocate "${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
assert_failure "pyenv-binary: found no interpreter to relocate under \`${BATS_TEST_TMPDIR}/prefix/bin'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "relocates an executable interpreter" {
|
||||||
|
stub_patchelf
|
||||||
|
create_interpreter
|
||||||
|
|
||||||
|
run pyenv-binary-relocate "${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
assert_success
|
||||||
|
run cat "${BATS_TEST_TMPDIR}/patchelf.log"
|
||||||
|
assert_output "--set-rpath ${BATS_TEST_TMPDIR}/prefix/lib ${BATS_TEST_TMPDIR}/prefix/bin/python2.7"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "relocates the extension modules but not what a wheel installed" {
|
||||||
|
local lib="${BATS_TEST_TMPDIR}/prefix/lib/python3.12"
|
||||||
|
stub_patchelf
|
||||||
|
create_interpreter
|
||||||
|
mkdir -p "${lib}/lib-dynload" "${lib}/site-packages/numpy"
|
||||||
|
touch "${lib}/lib-dynload/_ssl.cpython-312-x86_64-linux-gnu.so"
|
||||||
|
# A wheel points its extensions at the libraries it bundles alongside them, so
|
||||||
|
# its rpath is its own business and must survive relocation.
|
||||||
|
touch "${lib}/site-packages/numpy/_multiarray.so"
|
||||||
|
|
||||||
|
run pyenv-binary-relocate "${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
assert_success
|
||||||
|
run cat "${BATS_TEST_TMPDIR}/patchelf.log"
|
||||||
|
assert_line "--set-rpath ${BATS_TEST_TMPDIR}/prefix/lib ${lib}/lib-dynload/_ssl.cpython-312-x86_64-linux-gnu.so"
|
||||||
|
refute_line "--set-rpath ${BATS_TEST_TMPDIR}/prefix/lib ${lib}/site-packages/numpy/_multiarray.so"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "relocates macOS load commands without changing unrelated entries" {
|
||||||
|
local prefix="${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
local old="/build prefix/3.12.7"
|
||||||
|
local lib="${prefix}/lib/python3.12"
|
||||||
|
create_macos_tree
|
||||||
|
stub_macos_tools
|
||||||
|
|
||||||
|
run pyenv-binary-relocate "$prefix" "$old"
|
||||||
|
assert_success
|
||||||
|
run cat "${BATS_TEST_TMPDIR}/install-name-tool.log"
|
||||||
|
assert_output <<EOF
|
||||||
|
-change ${old}/lib/libpython3.12.dylib @rpath/libpython3.12.dylib ${prefix}/bin/python3.12
|
||||||
|
-rpath ${old}/lib @executable_path/../lib ${prefix}/bin/python3.12
|
||||||
|
-id @rpath/libpython3.12.dylib ${prefix}/lib/libpython3.12.dylib
|
||||||
|
-rpath ${old}/lib @loader_path ${prefix}/lib/libpython3.12.dylib
|
||||||
|
-rpath ${old}/lib @loader_path/../.. ${lib}/lib-dynload/_ssl.cpython-312-darwin.so
|
||||||
|
EOF
|
||||||
|
run grep -F "${lib}/site-packages/numpy/_multiarray.so" "${BATS_TEST_TMPDIR}/otool.log"
|
||||||
|
assert_failure
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "skips already relocated macOS load commands" {
|
||||||
|
local prefix="${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
create_macos_tree
|
||||||
|
stub_macos_tools
|
||||||
|
export OTOOL_RELOCATED=1
|
||||||
|
|
||||||
|
run pyenv-binary-relocate "$prefix" "/build prefix/3.12.7"
|
||||||
|
assert_success
|
||||||
|
assert [ ! -e "${BATS_TEST_TMPDIR}/install-name-tool.log" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when install_name_tool cannot modify a selected file" {
|
||||||
|
local prefix="${BATS_TEST_TMPDIR}/prefix"
|
||||||
|
create_macos_tree
|
||||||
|
stub_macos_tools
|
||||||
|
create_path_executable install_name_tool 'exit 1'
|
||||||
|
|
||||||
|
run pyenv-binary-relocate "$prefix" "/build prefix/3.12.7"
|
||||||
|
assert_failure ""
|
||||||
|
}
|
||||||
155
plugins/pyenv-binary/test/save.bats
Normal file
155
plugins/pyenv-binary/test/save.bats
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load test_helper
|
||||||
|
|
||||||
|
create_version() {
|
||||||
|
mkdir -p "${PYENV_ROOT}/versions/$1/bin"
|
||||||
|
}
|
||||||
|
|
||||||
|
platform() {
|
||||||
|
echo "$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails with no version given" {
|
||||||
|
run pyenv-binary-save
|
||||||
|
assert_failure "Usage: pyenv binary save <version> [<output-dir>] [--name <name>]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails for a version that is not installed" {
|
||||||
|
run pyenv-binary-save 9.9.9
|
||||||
|
assert_failure "pyenv-binary: version \`9.9.9' is not installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects a version name containing a slash" {
|
||||||
|
run pyenv-binary-save "foo/bar"
|
||||||
|
assert_failure "pyenv-binary: invalid version name \`foo/bar'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects the parent directory reference" {
|
||||||
|
run pyenv-binary-save ".."
|
||||||
|
assert_failure "pyenv-binary: invalid version name \`..'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "packages an installed version" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
local out="${BATS_TEST_TMPDIR}/dist"
|
||||||
|
local archive="${out}/3.12.7-$(platform).tar.gz"
|
||||||
|
|
||||||
|
run pyenv-binary-save "3.12.7" "$out"
|
||||||
|
assert_success "Saved 3.12.7-$(platform).tar.gz and 3.12.7-$(platform).meta to $out"
|
||||||
|
assert [ -f "$archive" ]
|
||||||
|
assert [ -f "${out}/3.12.7-$(platform).meta" ]
|
||||||
|
run tar -tzf "$archive"
|
||||||
|
assert_success
|
||||||
|
assert_line 0 "3.12.7/"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "uses an explicit package name" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
local out="${BATS_TEST_TMPDIR}/dist"
|
||||||
|
|
||||||
|
run pyenv-binary-save "3.12.7" "$out" --name "custom"
|
||||||
|
assert_success "Saved custom.tar.gz and custom.meta to $out"
|
||||||
|
assert [ -f "${out}/custom.tar.gz" ]
|
||||||
|
run grep '^archive=' "${out}/custom.meta"
|
||||||
|
assert_success "archive=custom.tar.gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when --name has no value" {
|
||||||
|
run pyenv-binary-save "3.12.7" --name
|
||||||
|
assert_failure "pyenv-binary: --name needs a value"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects an invalid package name" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
|
||||||
|
run pyenv-binary-save "3.12.7" --name "foo/bar"
|
||||||
|
assert_failure "pyenv-binary: invalid package name \`foo/bar'"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "records the platform in the metadata" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
local out="${BATS_TEST_TMPDIR}/dist"
|
||||||
|
pyenv-binary-save "3.12.7" "$out" >/dev/null
|
||||||
|
|
||||||
|
run cat "${out}/3.12.7-$(platform).meta"
|
||||||
|
assert_success
|
||||||
|
assert_line "version=3.12.7"
|
||||||
|
assert_line "platform=$(platform)"
|
||||||
|
assert_line "archive=3.12.7-$(platform).tar.gz"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "records the original installation prefix" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
local out="${BATS_TEST_TMPDIR}/dist"
|
||||||
|
pyenv-binary-save "3.12.7" "$out" >/dev/null
|
||||||
|
|
||||||
|
run grep '^build_prefix=' "${out}/3.12.7-$(platform).meta"
|
||||||
|
assert_success "build_prefix=${PYENV_ROOT}/versions/3.12.7"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "fails when readelf is not available" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
create_stub uname 'case "$1" in -s) echo Linux;; -m) echo x86_64;; esac'
|
||||||
|
|
||||||
|
PATH="$(path_without readelf)" run pyenv-binary-save "3.12.7" "${BATS_TEST_TMPDIR}/dist"
|
||||||
|
assert_failure "pyenv-binary: need readelf to inspect shared libraries"
|
||||||
|
assert [ ! -e "${BATS_TEST_TMPDIR}/dist/3.12.7-$(platform).tar.gz" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "records only direct libraries resolved outside the prefix" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
touch "${PYENV_ROOT}/versions/3.12.7/bin/python3.12"
|
||||||
|
create_path_executable uname <<'STUB'
|
||||||
|
case "$1" in
|
||||||
|
-s) echo Linux ;;
|
||||||
|
-m) echo x86_64 ;;
|
||||||
|
esac
|
||||||
|
STUB
|
||||||
|
create_path_executable ldd <<'STUB'
|
||||||
|
prefix="${PYENV_ROOT}/versions/3.12.7"
|
||||||
|
cat <<EOF
|
||||||
|
linux-vdso.so.1 (0x00007ffd1adfe000)
|
||||||
|
libpython3.12.so.1.0 => ${prefix}/lib/libpython3.12.so.1.0 (0x00007f4a3c000000)
|
||||||
|
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f4a3bc00000)
|
||||||
|
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4a3b800000)
|
||||||
|
/lib64/ld-linux-x86-64.so.2 (0x00007f4a3c200000)
|
||||||
|
EOF
|
||||||
|
STUB
|
||||||
|
create_path_executable readelf <<'STUB'
|
||||||
|
cat <<EOF
|
||||||
|
0x0000000000000001 (NEEDED) Shared library: [libpython3.12.so.1.0]
|
||||||
|
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
|
||||||
|
EOF
|
||||||
|
STUB
|
||||||
|
|
||||||
|
run pyenv-binary-save "3.12.7" "${BATS_TEST_TMPDIR}/dist"
|
||||||
|
assert_success
|
||||||
|
run grep '^dep=' "${BATS_TEST_TMPDIR}/dist/"*.meta
|
||||||
|
assert_output "dep=libm.so.6"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "records only the libraries otool resolves outside the prefix" {
|
||||||
|
create_version "3.12.7"
|
||||||
|
touch "${PYENV_ROOT}/versions/3.12.7/bin/python3.12"
|
||||||
|
create_path_executable uname <<'STUB'
|
||||||
|
case "$1" in
|
||||||
|
-s) echo Darwin ;;
|
||||||
|
-m) echo arm64 ;;
|
||||||
|
esac
|
||||||
|
STUB
|
||||||
|
create_path_executable otool <<'STUB'
|
||||||
|
prefix="${PYENV_ROOT}/versions/3.12.7"
|
||||||
|
cat <<EOF
|
||||||
|
${2}:
|
||||||
|
@rpath/libpython3.12.dylib (compatibility version 3.12.0, current version 3.12.0)
|
||||||
|
${prefix}/lib/libcrypto.3.dylib (compatibility version 3.0.0, current version 3.0.0)
|
||||||
|
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1345.0.0)
|
||||||
|
EOF
|
||||||
|
STUB
|
||||||
|
|
||||||
|
run pyenv-binary-save "3.12.7" "${BATS_TEST_TMPDIR}/dist"
|
||||||
|
assert_success
|
||||||
|
run grep '^dep=' "${BATS_TEST_TMPDIR}/dist/"*.meta
|
||||||
|
assert_output "dep=/usr/lib/libSystem.B.dylib"
|
||||||
|
}
|
||||||
1
plugins/pyenv-binary/test/test_helper.bash
Symbolic link
1
plugins/pyenv-binary/test/test_helper.bash
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../test/test_helper.bash
|
||||||
21
plugins/pyenv-link/LICENSE
Normal file
21
plugins/pyenv-link/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2023 yfprojects
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
33
plugins/pyenv-link/README.md
Normal file
33
plugins/pyenv-link/README.md
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# pyenv-link
|
||||||
|
A [pyenv](https://github.com/pyenv/pyenv) plugin for linking Python installations and virtual environments into your pyenv root.
|
||||||
|
|
||||||
|
This plugin recommends the [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv/) plugin.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Make an arbitrary virtualenv available through pyenv. This automatically guesses a fitting name from the prompt, the directory name or the location of the venv.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ pyenv link version .venv
|
||||||
|
Linked new version named myproject
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also specify a name to use for the venv.
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ pyenv link version .venv myname
|
||||||
|
Linked new version named myname
|
||||||
|
```
|
||||||
|
|
||||||
|
The same command can give a platform-specific binary installation a shorter name:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ pyenv link version "$(pyenv root)/versions/3.13.14-linux-x86_64" 3.13.14
|
||||||
|
Linked new version named 3.13.14
|
||||||
|
```
|
||||||
|
|
||||||
|
Now you can make pyenv activate/use the venv automatically:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ pyenv local myproject
|
||||||
|
```
|
||||||
34
plugins/pyenv-link/bin/pyenv-link
Executable file
34
plugins/pyenv-link/bin/pyenv-link
Executable file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Link a Python environment as a pyenv version
|
||||||
|
#
|
||||||
|
# Usage: pyenv link version [--dry] [--quiet] <path> [<name>]
|
||||||
|
#
|
||||||
|
# Link a virtual python environment to pyenvs version directory
|
||||||
|
# so that the venv can be used like one created with *pyenv-virtualenv*.
|
||||||
|
#
|
||||||
|
# <path> should be a path to a Python installation or virtual environment.
|
||||||
|
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
# Provide pyenv completions
|
||||||
|
case "$1" in
|
||||||
|
--complete)
|
||||||
|
if [ -z "$2" ]; then
|
||||||
|
echo version
|
||||||
|
else
|
||||||
|
shift 2
|
||||||
|
pyenv-link-version --complete "$@"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
version)
|
||||||
|
shift
|
||||||
|
pyenv-link-version "$@"
|
||||||
|
exit $?
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
pyenv-help --usage link >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
150
plugins/pyenv-link/bin/pyenv-link-version
Executable file
150
plugins/pyenv-link/bin/pyenv-link-version
Executable file
|
|
@ -0,0 +1,150 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Link a Python environment as a pyenv version
|
||||||
|
#
|
||||||
|
# Usage: pyenv link version [--dry] [--quiet] <path> [<name>]
|
||||||
|
#
|
||||||
|
# Link a virtual python environment to pyenvs version directory
|
||||||
|
# so that the venv can be used like one created with *pyenv-virtualenv*.
|
||||||
|
#
|
||||||
|
# <path> should be a path to a Python installation or virtual environment.
|
||||||
|
# <name> should be string used as a version name it may not be present
|
||||||
|
# in the pyenv version directory yet.
|
||||||
|
# If not specified a matching name is guessed from pyvenv.cfg
|
||||||
|
# or directory name of the venv.
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
# functions
|
||||||
|
|
||||||
|
if ! {
|
||||||
|
enable -f "${BASH_SOURCE%/*}"/../../../libexec/pyenv-realpath.dylib realpath ||
|
||||||
|
type realpath # realpath available?
|
||||||
|
} >/dev/null 2>&1; then
|
||||||
|
# realpath requires GNU coreutils to be installed. That's why its bundled with pyenv
|
||||||
|
echo realpath not available >&2
|
||||||
|
|
||||||
|
# work-around when realpath is unavailable
|
||||||
|
realpath() {
|
||||||
|
(cd "$1" && pwd)
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
guess_name() {
|
||||||
|
local venv_dir=$1
|
||||||
|
local name
|
||||||
|
|
||||||
|
# guess venv name from pyvenv.cfg
|
||||||
|
local pyvenv_cfg_path=$venv_dir/pyvenv.cfg
|
||||||
|
if [ -f "$pyvenv_cfg_path" ]; then
|
||||||
|
# if `prompt` key wasn't found the var remains empty
|
||||||
|
name=$(cut -b 1-1024 "$pyvenv_cfg_path" | sed -n '/^ *prompt *= */s///p')
|
||||||
|
case "$name" in
|
||||||
|
\"*\" | \'*\')
|
||||||
|
name=${name:1:${#name}-2}
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# guess venv name from name of venv directory
|
||||||
|
local venv_dir_name=${venv_dir##*/}
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
case "$venv_dir_name" in
|
||||||
|
venv | env | .venv | .env | ENV | VENV) ;;
|
||||||
|
*) name=$venv_dir_name ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# guess venv name from name of the parent directory of the venv directory
|
||||||
|
local venv_dir_parent=${venv_dir%/*}
|
||||||
|
local venv_dir_parent_name=${venv_dir_parent##*/}
|
||||||
|
if [ -z "$name" ]; then
|
||||||
|
name=$venv_dir_parent_name
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$name"
|
||||||
|
}
|
||||||
|
|
||||||
|
# process args
|
||||||
|
POSARGS=()
|
||||||
|
# Provide pyenv completions
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
echo --dry
|
||||||
|
echo --quiet
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
for arg in "$@"; do
|
||||||
|
case $arg in
|
||||||
|
--dry)
|
||||||
|
dry=true # any value
|
||||||
|
;;
|
||||||
|
--quiet)
|
||||||
|
quiet=true # any value
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
pyenv-help --usage link-version >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
POSARGS+=("$arg")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! ${#POSARGS[@]} -le 2 ] || [ ! ${#POSARGS[@]} -ge 1 ]; then
|
||||||
|
pyenv-help --usage link-version >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
venv_dir=${POSARGS[0]%/}
|
||||||
|
venv_name=${POSARGS[1]}
|
||||||
|
|
||||||
|
# Check venv exists
|
||||||
|
if [ ! -d "$venv_dir" ]; then
|
||||||
|
echo "The virtual env you specified doesn't exist"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# make venv dir absolute
|
||||||
|
venv_dir=$(realpath "$venv_dir")
|
||||||
|
|
||||||
|
# determine venv name
|
||||||
|
if [ -z "$venv_name" ]; then
|
||||||
|
venv_name=$(guess_name "$venv_dir")
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$venv_name" in
|
||||||
|
"" | */* | . | .. | *:* | system | *[[:cntrl:]]* )
|
||||||
|
echo "pyenv-link: invalid version name \`${venv_name}'" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
versions_dir="$PYENV_ROOT/versions"
|
||||||
|
pyenv_prefix_path="$versions_dir/$venv_name"
|
||||||
|
|
||||||
|
if [ -e "$pyenv_prefix_path" ] || [ -L "$pyenv_prefix_path" ]; then
|
||||||
|
echo Version "$venv_name" already exists >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$venv_dir" in
|
||||||
|
"$versions_dir"/*)
|
||||||
|
link_target=${venv_dir#"$versions_dir"/}
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
link_target=$venv_dir
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# link to pyenv version directory
|
||||||
|
if [ -z "$dry" ]; then
|
||||||
|
mkdir -p "$versions_dir"
|
||||||
|
ln -s "$link_target" "$pyenv_prefix_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# output
|
||||||
|
if [ -z "$quiet" ]; then
|
||||||
|
echo "Linked new version named $venv_name"
|
||||||
|
fi
|
||||||
61
plugins/pyenv-link/test/link.bats
Normal file
61
plugins/pyenv-link/test/link.bats
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/usr/bin/env bats
|
||||||
|
|
||||||
|
load test_helper
|
||||||
|
|
||||||
|
@test "link completions use the dispatcher" {
|
||||||
|
run pyenv completions link
|
||||||
|
assert_success
|
||||||
|
assert_output <<OUT
|
||||||
|
--help
|
||||||
|
version
|
||||||
|
OUT
|
||||||
|
run pyenv completions link version
|
||||||
|
assert_success
|
||||||
|
assert_output <<OUT
|
||||||
|
--help
|
||||||
|
--dry
|
||||||
|
--quiet
|
||||||
|
OUT
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "link requires a version path" {
|
||||||
|
run pyenv-link version
|
||||||
|
assert_failure "Usage: pyenv link version [--dry] [--quiet] <path> [<name>]"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "links an installed binary under its plain version name" {
|
||||||
|
create_alt_executable_in_version "3.13.14-linux-x86_64" python 'echo linked-python'
|
||||||
|
run pyenv-link version "$PYENV_ROOT/versions/3.13.14-linux-x86_64" 3.13.14
|
||||||
|
assert_success "Linked new version named 3.13.14"
|
||||||
|
assert_equal "3.13.14-linux-x86_64" "$(readlink "$PYENV_ROOT/versions/3.13.14")"
|
||||||
|
PYENV_VERSION=3.13.14 run pyenv exec python
|
||||||
|
assert_success "linked-python"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "links external paths containing spaces into a fresh root" {
|
||||||
|
mkdir -p "$PYENV_TEST_DIR/external env/bin"
|
||||||
|
run pyenv-link version "$PYENV_TEST_DIR/external env" custom
|
||||||
|
assert_success "Linked new version named custom"
|
||||||
|
assert_equal "$PYENV_TEST_DIR/external env" "$(readlink "$PYENV_ROOT/versions/custom")"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "refuses a dangling destination link" {
|
||||||
|
mkdir -p "$PYENV_ROOT/versions" "$PYENV_TEST_DIR/source"
|
||||||
|
ln -s missing "$PYENV_ROOT/versions/dangling"
|
||||||
|
run pyenv-link version "$PYENV_TEST_DIR/source" dangling
|
||||||
|
assert_failure "Version dangling already exists"
|
||||||
|
assert_equal missing "$(readlink "$PYENV_ROOT/versions/dangling")"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "rejects invalid version names" {
|
||||||
|
mkdir -p "$PYENV_TEST_DIR/source"
|
||||||
|
for name in ../outside . .. 'foo/bar' 'foo:bar' system; do
|
||||||
|
run pyenv-link version "$PYENV_TEST_DIR/source" "$name"
|
||||||
|
assert_failure "pyenv-link: invalid version name \`$name'"
|
||||||
|
assert [ ! -e "$PYENV_ROOT/outside" ]
|
||||||
|
done
|
||||||
|
echo "prompt = '../outside'" > "$PYENV_TEST_DIR/source/pyvenv.cfg"
|
||||||
|
run pyenv-link version "$PYENV_TEST_DIR/source"
|
||||||
|
assert_failure "pyenv-link: invalid version name \`../outside'"
|
||||||
|
assert [ ! -e "$PYENV_ROOT/outside" ]
|
||||||
|
}
|
||||||
1
plugins/pyenv-link/test/test_helper.bash
Symbolic link
1
plugins/pyenv-link/test/test_helper.bash
Symbolic link
|
|
@ -0,0 +1 @@
|
||||||
|
../../../test/test_helper.bash
|
||||||
|
|
@ -54,8 +54,11 @@ Or, if you would like to install the latest development release:
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Before you begin, you should ensure that your build environment has the proper
|
On Ubuntu, Debian, and Mint, install the recommended build dependencies with:
|
||||||
system dependencies for compiling the wanted Python Version (see our [recommendations](https://github.com/pyenv/pyenv/wiki#suggested-build-environment)).
|
|
||||||
|
pyenv install-prerequisites
|
||||||
|
|
||||||
|
For other environments, see our [build environment recommendations](https://github.com/pyenv/pyenv/wiki#suggested-build-environment).
|
||||||
|
|
||||||
### Using `pyenv install` with pyenv
|
### Using `pyenv install` with pyenv
|
||||||
|
|
||||||
|
|
@ -67,6 +70,13 @@ exact name of the version you want to install. For example,
|
||||||
Python versions will be installed into a directory of the same name under
|
Python versions will be installed into a directory of the same name under
|
||||||
`~/.pyenv/versions`.
|
`~/.pyenv/versions`.
|
||||||
|
|
||||||
|
To install a version under a different name -- for instance, to keep several
|
||||||
|
builds of the same version side by side -- append `:<alias>` to the version:
|
||||||
|
|
||||||
|
pyenv install 3.12.0:3.12-custom
|
||||||
|
|
||||||
|
This installs into `~/.pyenv/versions/3.12-custom`.
|
||||||
|
|
||||||
To see a list of all available Python versions, run `pyenv install --list`. You
|
To see a list of all available Python versions, run `pyenv install --list`. You
|
||||||
may also tab-complete available Python versions if your pyenv installation is
|
may also tab-complete available Python versions if your pyenv installation is
|
||||||
properly configured.
|
properly configured.
|
||||||
|
|
@ -262,7 +272,7 @@ would be to make symlinks at the mirror's root:
|
||||||
```
|
```
|
||||||
|
|
||||||
The rationale is to abstract away difference between directory structures of sites
|
The rationale is to abstract away difference between directory structures of sites
|
||||||
of various Python flavors and their occasional changes as well as to accomodate
|
of various Python flavors and their occasional changes as well as to accommodate
|
||||||
people who only wish to cache some select downloads. This also allows to mirror multiple sites at once.
|
people who only wish to cache some select downloads. This also allows to mirror multiple sites at once.
|
||||||
|
|
||||||
If the mirror being used does not have the same checksum (*e.g.* with a
|
If the mirror being used does not have the same checksum (*e.g.* with a
|
||||||
|
|
@ -354,4 +364,3 @@ git diff --name-only master \
|
||||||
- Filter out any which don't live where python-build keeps its build scripts
|
- Filter out any which don't live where python-build keeps its build scripts
|
||||||
- Look only at the file name (i.e. the python version name)
|
- Look only at the file name (i.e. the python version name)
|
||||||
- Run a new docker container for each, building that version
|
- Run a new docker container for each, building that version
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
#
|
#
|
||||||
# Summary: Install a Python version using python-build
|
# Summary: Install a Python version using python-build
|
||||||
#
|
#
|
||||||
# Usage: pyenv install [-f] [-kvp] <version>...
|
# Usage: pyenv install [-f] [-kvp] <version>[:<alias>]...
|
||||||
# pyenv install [-f] [-kvp] <definition-file>
|
# pyenv install [-f] [-kvp] <definition-file>[:<alias>]
|
||||||
# pyenv install -l|--list
|
# pyenv install -l|--list [--bare]
|
||||||
# pyenv install --version
|
# pyenv install --version
|
||||||
#
|
#
|
||||||
# -l/--list List all available versions
|
# -l/--list List all available versions
|
||||||
|
|
@ -20,6 +20,13 @@
|
||||||
# --version Show version of python-build
|
# --version Show version of python-build
|
||||||
# -g/--debug Build a debug version
|
# -g/--debug Build a debug version
|
||||||
#
|
#
|
||||||
|
# Append `:<alias>' to a version to install it under a custom name, so that
|
||||||
|
# several builds of the same version can coexist:
|
||||||
|
#
|
||||||
|
# pyenv install 3.12.0:my-3.12
|
||||||
|
#
|
||||||
|
# This installs into $PYENV_ROOT/versions/my-3.12.
|
||||||
|
#
|
||||||
# For detailed information on installing Python versions with
|
# For detailed information on installing Python versions with
|
||||||
# python-build, including a list of environment variables for adjusting
|
# python-build, including a list of environment variables for adjusting
|
||||||
# compilation, see: https://github.com/pyenv/pyenv#readme
|
# compilation, see: https://github.com/pyenv/pyenv#readme
|
||||||
|
|
@ -38,6 +45,7 @@ shopt -u nullglob
|
||||||
|
|
||||||
# Provide pyenv completions
|
# Provide pyenv completions
|
||||||
if [ "$1" = "--complete" ]; then
|
if [ "$1" = "--complete" ]; then
|
||||||
|
echo --bare
|
||||||
echo --list
|
echo --list
|
||||||
echo --force
|
echo --force
|
||||||
echo --skip-existing
|
echo --skip-existing
|
||||||
|
|
@ -72,6 +80,8 @@ unset KEEP
|
||||||
unset VERBOSE
|
unset VERBOSE
|
||||||
unset HAS_PATCH
|
unset HAS_PATCH
|
||||||
unset DEBUG
|
unset DEBUG
|
||||||
|
unset BARE
|
||||||
|
unset LIST
|
||||||
|
|
||||||
[ -n "$PYENV_DEBUG" ] && VERBOSE="-v"
|
[ -n "$PYENV_DEBUG" ] && VERBOSE="-v"
|
||||||
|
|
||||||
|
|
@ -81,10 +91,11 @@ for option in "${OPTIONS[@]}"; do
|
||||||
"h" | "help" )
|
"h" | "help" )
|
||||||
usage 0
|
usage 0
|
||||||
;;
|
;;
|
||||||
|
"bare" )
|
||||||
|
BARE=1
|
||||||
|
;;
|
||||||
"l" | "list" )
|
"l" | "list" )
|
||||||
echo "Available versions:"
|
LIST=1
|
||||||
definitions | indent
|
|
||||||
exit
|
|
||||||
;;
|
;;
|
||||||
"f" | "force" )
|
"f" | "force" )
|
||||||
FORCE=true
|
FORCE=true
|
||||||
|
|
@ -113,6 +124,16 @@ for option in "${OPTIONS[@]}"; do
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ -n $LIST ]]; then
|
||||||
|
if [[ -n $BARE ]]; then
|
||||||
|
definitions
|
||||||
|
else
|
||||||
|
echo "Available versions:"
|
||||||
|
definitions | indent
|
||||||
|
fi
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
unset VERSION_NAME
|
unset VERSION_NAME
|
||||||
|
|
||||||
# The first argument contains the definition to install. If the
|
# The first argument contains the definition to install. If the
|
||||||
|
|
@ -123,6 +144,20 @@ DEFINITIONS=("${ARGUMENTS[@]}")
|
||||||
[[ "${#DEFINITIONS[*]}" -eq 0 ]] && DEFINITIONS=($(pyenv-local 2>/dev/null || true))
|
[[ "${#DEFINITIONS[*]}" -eq 0 ]] && DEFINITIONS=($(pyenv-local 2>/dev/null || true))
|
||||||
[[ "${#DEFINITIONS[*]}" -eq 0 ]] && usage 1 >&2
|
[[ "${#DEFINITIONS[*]}" -eq 0 ]] && usage 1 >&2
|
||||||
|
|
||||||
|
# A `<version>:<alias>` argument installs <version> under the custom name
|
||||||
|
# <alias>, so that several builds of the same version can coexist. The `latest`
|
||||||
|
# suffix is reserved for latest-version resolution (see the `install` hook), so
|
||||||
|
# it is left untouched here.
|
||||||
|
declare -a ALIASES
|
||||||
|
for i in "${!DEFINITIONS[@]}"; do
|
||||||
|
definition="${DEFINITIONS[$i]}"
|
||||||
|
alias="${definition##*:}"
|
||||||
|
if [[ "$definition" == *:* ]] && [ "$alias" != "latest" ]; then
|
||||||
|
DEFINITIONS[$i]="${definition%:*}"
|
||||||
|
ALIASES[$i]="$alias"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# Define `before_install` and `after_install` functions that allow
|
# Define `before_install` and `after_install` functions that allow
|
||||||
# plugin hooks to register a string of code for execution before or
|
# plugin hooks to register a string of code for execution before or
|
||||||
# after the installation process.
|
# after the installation process.
|
||||||
|
|
@ -152,7 +187,9 @@ IFS="$OLDIFS"
|
||||||
for script in "${scripts[@]}"; do source "$script"; done
|
for script in "${scripts[@]}"; do source "$script"; done
|
||||||
|
|
||||||
COMBINED_STATUS=0
|
COMBINED_STATUS=0
|
||||||
for DEFINITION in "${DEFINITIONS[@]}"; do
|
for i in "${!DEFINITIONS[@]}"; do
|
||||||
|
DEFINITION="${DEFINITIONS[$i]}"
|
||||||
|
VERSION_ALIAS="${ALIASES[$i]}"
|
||||||
STATUS=0
|
STATUS=0
|
||||||
|
|
||||||
# Try to resolve a prefix if user indeed gave a prefix.
|
# Try to resolve a prefix if user indeed gave a prefix.
|
||||||
|
|
@ -161,9 +198,12 @@ for DEFINITION in "${DEFINITIONS[@]}"; do
|
||||||
DEFINITION="$(pyenv-latest -f -k "$DEFINITION")"
|
DEFINITION="$(pyenv-latest -f -k "$DEFINITION")"
|
||||||
|
|
||||||
# Set VERSION_NAME from $DEFINITION. Then compute the installation prefix.
|
# Set VERSION_NAME from $DEFINITION. Then compute the installation prefix.
|
||||||
|
# With a `<version>:<alias>` argument, install under the alias instead;
|
||||||
|
# VERSION_NAME still reflects the real version so version-specific build logic
|
||||||
|
# (e.g. the bootstrap version detection below) keeps working.
|
||||||
VERSION_NAME="${DEFINITION##*/}"
|
VERSION_NAME="${DEFINITION##*/}"
|
||||||
[ -n "$DEBUG" ] && VERSION_NAME="${VERSION_NAME}-debug"
|
[ -n "$DEBUG" ] && VERSION_NAME="${VERSION_NAME}-debug"
|
||||||
PREFIX="${PYENV_ROOT}/versions/${VERSION_NAME}"
|
PREFIX="${PYENV_ROOT}/versions/${VERSION_ALIAS:-$VERSION_NAME}"
|
||||||
|
|
||||||
[ -d "${PREFIX}" ] && PREFIX_EXISTS=1
|
[ -d "${PREFIX}" ] && PREFIX_EXISTS=1
|
||||||
|
|
||||||
|
|
@ -188,7 +228,7 @@ for DEFINITION in "${DEFINITIONS[@]}"; do
|
||||||
|
|
||||||
# If PYENV_BUILD_ROOT is set, always pass keep options to python-build.
|
# If PYENV_BUILD_ROOT is set, always pass keep options to python-build.
|
||||||
if [ -n "${PYENV_BUILD_ROOT}" ]; then
|
if [ -n "${PYENV_BUILD_ROOT}" ]; then
|
||||||
export PYTHON_BUILD_BUILD_PATH="${PYENV_BUILD_ROOT}/${VERSION_NAME}"
|
export PYTHON_BUILD_BUILD_PATH="${PYENV_BUILD_ROOT}/${VERSION_ALIAS:-$VERSION_NAME}"
|
||||||
KEEP="-k"
|
KEEP="-k"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
||||||
47
plugins/python-build/bin/pyenv-install-prerequisites
Executable file
47
plugins/python-build/bin/pyenv-install-prerequisites
Executable file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# Summary: Install Python build prerequisites on Debian-based systems
|
||||||
|
#
|
||||||
|
# Usage: pyenv install-prerequisites
|
||||||
|
#
|
||||||
|
set -e
|
||||||
|
[ -n "$PYENV_DEBUG" ] && set -x
|
||||||
|
|
||||||
|
# Provide pyenv completions
|
||||||
|
if [ "$1" = "--complete" ]; then
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
pyenv-help install-prerequisites 2>/dev/null
|
||||||
|
[ -z "$1" ] || exit "$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
|
||||||
|
usage 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$#" -eq 0 ] || usage 1 >&2
|
||||||
|
|
||||||
|
if ! command -v apt-get >/dev/null; then
|
||||||
|
echo "pyenv: installing build prerequisites is not supported on this system" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
prerequisites=(
|
||||||
|
make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev
|
||||||
|
libsqlite3-dev curl git llvm libncurses5-dev libncursesw5-dev xz-utils
|
||||||
|
tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev libzstd-dev
|
||||||
|
)
|
||||||
|
|
||||||
|
if [ "$(id -u)" -eq 0 ]; then
|
||||||
|
apt_get=(apt-get)
|
||||||
|
elif command -v sudo >/dev/null; then
|
||||||
|
apt_get=(sudo apt-get)
|
||||||
|
else
|
||||||
|
echo "pyenv: sudo is required to install build prerequisites" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
"${apt_get[@]}" update -q
|
||||||
|
"${apt_get[@]}" install -yq "${prerequisites[@]}"
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
# -g/--debug Build a debug version
|
# -g/--debug Build a debug version
|
||||||
#
|
#
|
||||||
|
|
||||||
PYTHON_BUILD_VERSION="2.7.1"
|
PYTHON_BUILD_VERSION="2.8.5"
|
||||||
|
|
||||||
OLDIFS="$IFS"
|
OLDIFS="$IFS"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import os.path
|
||||||
import pathlib
|
import pathlib
|
||||||
import pprint
|
import pprint
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import typing
|
import typing
|
||||||
|
|
@ -30,13 +31,6 @@ import requests_html
|
||||||
import sortedcontainers
|
import sortedcontainers
|
||||||
import tqdm
|
import tqdm
|
||||||
|
|
||||||
#CI uses exit code 1 as a signal that no new version is found
|
|
||||||
#so have to produce a different exit code on an exception
|
|
||||||
def _excepthook(type,value,traceback):
|
|
||||||
logging.error("Unhandled exception occured",exc_info=(type,value,traceback))
|
|
||||||
sys.exit(2)
|
|
||||||
sys.excepthook = _excepthook
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
CUTOFF_VERSION=packaging.version.Version('3.10')
|
CUTOFF_VERSION=packaging.version.Version('3.10')
|
||||||
|
|
@ -46,6 +40,14 @@ EXCLUDED_VERSIONS= {
|
||||||
here = pathlib.Path(__file__).resolve()
|
here = pathlib.Path(__file__).resolve()
|
||||||
OUT_DIR: pathlib.Path = here.parent.parent / "share" / "python-build"
|
OUT_DIR: pathlib.Path = here.parent.parent / "share" / "python-build"
|
||||||
|
|
||||||
|
AUTO_ADD_VERSION_REF_RE = re.compile(
|
||||||
|
r"^(?P<object_id>[0-9a-f]{40,64})\t"
|
||||||
|
r"refs/heads/auto_add_version/(?P<versions>\S+)$"
|
||||||
|
)
|
||||||
|
OPENSSL_RELEASE_TAG_RE = re.compile(
|
||||||
|
r"^openssl-(?P<major>\d+)\.\d+(?:\.\d+)*(?:[a-z]+\d*)?$"
|
||||||
|
)
|
||||||
|
|
||||||
T_THUNK=\
|
T_THUNK=\
|
||||||
'''export PYTHON_BUILD_FREE_THREADING=1
|
'''export PYTHON_BUILD_FREE_THREADING=1
|
||||||
source "${BASH_SOURCE[0]%t}"
|
source "${BASH_SOURCE[0]%t}"
|
||||||
|
|
@ -83,10 +85,13 @@ def adapt_script(version: packaging.version.Version,
|
||||||
url=new_package_url+'#'+new_package_hash,
|
url=new_package_url+'#'+new_package_hash,
|
||||||
verify_py_suffix=verify_py_suffix)
|
verify_py_suffix=verify_py_suffix)
|
||||||
|
|
||||||
elif m:=re.match(r'\s*install_package\s+"(?P<package>openssl-\S+)"\s+'
|
elif m:=re.match(r'\s*install_package\s+'
|
||||||
r'"(?P<url>\S+)"\s.*$',
|
r'"(?P<package>openssl-(?P<openssl_major>\d+)\.\S+)"\s+'
|
||||||
|
r'"(?P<url>\S+)"\s.*$',
|
||||||
line):
|
line):
|
||||||
item = VersionDirectory.openssl.get_store_latest_release()
|
item = VersionDirectory.openssl.get_store_latest_release(
|
||||||
|
int(m.group('openssl_major'))
|
||||||
|
)
|
||||||
|
|
||||||
line = Re.sub_groups(m,
|
line = Re.sub_groups(m,
|
||||||
package=item.package_name,
|
package=item.package_name,
|
||||||
|
|
@ -129,6 +134,8 @@ def add_version(version: packaging.version.Version):
|
||||||
return False
|
return False
|
||||||
VersionDirectory.existing.append(_CPythonExistingScriptInfo(version,str(new_path)))
|
VersionDirectory.existing.append(_CPythonExistingScriptInfo(version,str(new_path)))
|
||||||
|
|
||||||
|
handle_version_patches(version, previous_version, is_prerelease_upgrade)
|
||||||
|
|
||||||
cleanup_prerelease_upgrade(is_prerelease_upgrade, previous_version, version)
|
cleanup_prerelease_upgrade(is_prerelease_upgrade, previous_version, version)
|
||||||
|
|
||||||
handle_t_thunks(version, previous_version, is_prerelease_upgrade)
|
handle_t_thunks(version, previous_version, is_prerelease_upgrade)
|
||||||
|
|
@ -137,6 +144,52 @@ def add_version(version: packaging.version.Version):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def handle_version_patches(
|
||||||
|
version: packaging.version.Version,
|
||||||
|
previous_version: packaging.version.Version,
|
||||||
|
is_prerelease_upgrade: bool)\
|
||||||
|
-> None:
|
||||||
|
if (previous_version.major, previous_version.minor) != (version.major, version.minor):
|
||||||
|
return
|
||||||
|
|
||||||
|
patches_dir = OUT_DIR / "patches"
|
||||||
|
previous_patches = patches_dir / str(previous_version)
|
||||||
|
if not previous_patches.exists():
|
||||||
|
return
|
||||||
|
|
||||||
|
new_patches = patches_dir / str(version)
|
||||||
|
if is_prerelease_upgrade:
|
||||||
|
logger.info(f"Git moving patches from {previous_version} to {version}")
|
||||||
|
subprocess.check_call((
|
||||||
|
"git", "-C", OUT_DIR, "mv",
|
||||||
|
f"patches/{previous_version}",
|
||||||
|
f"patches/{version}",
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
logger.info(f"Copying patches from {previous_version} to {version}")
|
||||||
|
shutil.copytree(previous_patches, new_patches)
|
||||||
|
|
||||||
|
# Subdir rename as a separate step from upper dir copying/moving
|
||||||
|
# in case there are patches for dependency packages as well
|
||||||
|
previous_package_patches = new_patches / f"Python-{previous_version}"
|
||||||
|
new_package_patches = new_patches / f"Python-{version}"
|
||||||
|
if is_prerelease_upgrade:
|
||||||
|
subprocess.check_call((
|
||||||
|
"git", "-C", OUT_DIR, "mv",
|
||||||
|
f"patches/{version}/Python-{previous_version}",
|
||||||
|
f"patches/{version}/Python-{version}",
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
previous_package_patches.rename(new_package_patches)
|
||||||
|
|
||||||
|
if uses_t_thunks(previous_version) and is_prerelease_upgrade:
|
||||||
|
(patches_dir / f"{previous_version}t").unlink(missing_ok=True)
|
||||||
|
if uses_t_thunks(version):
|
||||||
|
(patches_dir / f"{version}t").symlink_to(
|
||||||
|
str(version), target_is_directory=True
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def cleanup_prerelease_upgrade(
|
def cleanup_prerelease_upgrade(
|
||||||
is_prerelease_upgrade: bool,
|
is_prerelease_upgrade: bool,
|
||||||
previous_version: packaging.version.Version,
|
previous_version: packaging.version.Version,
|
||||||
|
|
@ -166,7 +219,7 @@ def cleanup_prerelease_upgrade(
|
||||||
|
|
||||||
|
|
||||||
def handle_t_thunks(version, previous_version, is_prerelease_upgrade):
|
def handle_t_thunks(version, previous_version, is_prerelease_upgrade):
|
||||||
if (version.major, version.minor) < (3, 13):
|
if not uses_t_thunks(version):
|
||||||
return
|
return
|
||||||
|
|
||||||
# an old thunk may have older version-specific code
|
# an old thunk may have older version-specific code
|
||||||
|
|
@ -185,6 +238,10 @@ def handle_t_thunks(version, previous_version, is_prerelease_upgrade):
|
||||||
thunk_path.write_text(T_THUNK, encoding='utf-8')
|
thunk_path.write_text(T_THUNK, encoding='utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
def uses_t_thunks(version: packaging.version.Version) -> bool:
|
||||||
|
return (version.major, version.minor) >= (3, 13)
|
||||||
|
|
||||||
|
|
||||||
Arguments: argparse.Namespace
|
Arguments: argparse.Namespace
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
@ -203,15 +260,25 @@ def main():
|
||||||
# So until we know the release is out, its directory is a potential prerelease directory.
|
# So until we know the release is out, its directory is a potential prerelease directory.
|
||||||
# Normally, prereleases are only made for initial releases (x.y.0) --
|
# Normally, prereleases are only made for initial releases (x.y.0) --
|
||||||
# but rarely, they may make them for other releases (e.g. 3.14.5).
|
# but rarely, they may make them for other releases (e.g. 3.14.5).
|
||||||
for release in (v for v in frozenset(VersionDirectory.available.keys()) #refining changes the
|
for release in (v for v in frozenset(VersionDirectory.available.keys()) #refining alters the
|
||||||
#corresponding directory key
|
#corresponding directory key
|
||||||
#which breaks iteration
|
#which breaks iteration
|
||||||
|
#over the directory --
|
||||||
#so have to iterate over a copy
|
#so have to iterate over a copy
|
||||||
if v not in VersionDirectory.existing):
|
if v not in VersionDirectory.existing):
|
||||||
VersionDirectory.available.get_store_available_source_downloads(release, True)
|
VersionDirectory.available.get_store_available_source_downloads(release, True)
|
||||||
del release
|
del release
|
||||||
|
|
||||||
versions_to_add = sorted(VersionDirectory.available.keys() - VersionDirectory.existing.keys())
|
# Excluding versions for which there already are PRs.
|
||||||
|
# This will prevent us from using advanced features of
|
||||||
|
# peter-evans/create-pull-request Github Action
|
||||||
|
# like updating a PR and closing a superseded PR
|
||||||
|
# but we don't really need them as of this writing.
|
||||||
|
versions_to_add = sorted(
|
||||||
|
VersionDirectory.available.keys()
|
||||||
|
- VersionDirectory.existing.keys()
|
||||||
|
- get_pending_versions()
|
||||||
|
)
|
||||||
|
|
||||||
logger.info("Versions to add:\n"+pprint.pformat(versions_to_add))
|
logger.info("Versions to add:\n"+pprint.pformat(versions_to_add))
|
||||||
result = False
|
result = False
|
||||||
|
|
@ -219,6 +286,26 @@ def main():
|
||||||
result = add_version(version_to_add) or result
|
result = add_version(version_to_add) or result
|
||||||
return int(not result)
|
return int(not result)
|
||||||
|
|
||||||
|
|
||||||
|
def get_pending_versions() -> typing.Set[packaging.version.Version]:
|
||||||
|
ls_remote = subprocess.check_output(
|
||||||
|
("git", "-C", OUT_DIR, "ls-remote", "origin",
|
||||||
|
"refs/heads/auto_add_version/*"),
|
||||||
|
text=True,
|
||||||
|
timeout=30,
|
||||||
|
)
|
||||||
|
|
||||||
|
pending_versions = set()
|
||||||
|
for line in ls_remote.splitlines():
|
||||||
|
if not (match := AUTO_ADD_VERSION_REF_RE.fullmatch(line)):
|
||||||
|
raise ValueError(f"Unexpected git ls-remote output line: {line!r}")
|
||||||
|
pending_versions.update(
|
||||||
|
packaging.version.Version(version)
|
||||||
|
for version in match.group("versions").split("_")
|
||||||
|
)
|
||||||
|
return pending_versions
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
|
|
@ -369,8 +456,9 @@ class CPythonAvailableVersionsDirectory(KeyedList[_CPythonAvailableVersionInfo,
|
||||||
download_version = packaging.version.Version(m.group("version"))
|
download_version = packaging.version.Version(m.group("version"))
|
||||||
if download_version != version:
|
if download_version != version:
|
||||||
if not refine_mode:
|
if not refine_mode:
|
||||||
raise ValueError(f"Unexpectedly found a download {name} ({download_version}) "
|
logger.warning(f"Ignoring download {name} ({download_version}) "
|
||||||
f"for {version} at page {entry.download_page_url}")
|
f"for {version} at page {entry.download_page_url}")
|
||||||
|
continue
|
||||||
entry_to_fill = additional_versions_found.get_or_create(
|
entry_to_fill = additional_versions_found.get_or_create(
|
||||||
download_version,
|
download_version,
|
||||||
download_page_url=entry.download_page_url
|
download_page_url=entry.download_page_url
|
||||||
|
|
@ -383,7 +471,10 @@ class CPythonAvailableVersionsDirectory(KeyedList[_CPythonAvailableVersionInfo,
|
||||||
m.group("extension"), m.group('package'), url
|
m.group("extension"), m.group('package'), url
|
||||||
))
|
))
|
||||||
|
|
||||||
if not exact_download_found:
|
# XXX: Exact download not found in non-refine mode never happens now
|
||||||
|
# 'cuz we first call the function in refine mode.
|
||||||
|
# Decide what's best to do if it starts to after a logic change.
|
||||||
|
if not exact_download_found and refine_mode:
|
||||||
actual_version = max(additional_versions_found.keys())
|
actual_version = max(additional_versions_found.keys())
|
||||||
logger.debug(f"Refining available version {version} to {actual_version}")
|
logger.debug(f"Refining available version {version} to {actual_version}")
|
||||||
del self[version]
|
del self[version]
|
||||||
|
|
@ -415,7 +506,7 @@ class CPythonExistingScriptsDirectory(KeyedList[_CPythonExistingScriptInfo, pack
|
||||||
v = packaging.version.Version(entry_name)
|
v = packaging.version.Version(entry_name)
|
||||||
if v < CUTOFF_VERSION:
|
if v < CUTOFF_VERSION:
|
||||||
continue
|
continue
|
||||||
# branch tip scrpts are different from release scripts and thus unusable as a pattern
|
# branch tip scripts are different from release scripts and thus unusable as a pattern
|
||||||
if v.dev is not None:
|
if v.dev is not None:
|
||||||
continue
|
continue
|
||||||
logger.debug(f"Existing version {v}")
|
logger.debug(f"Existing version {v}")
|
||||||
|
|
@ -440,21 +531,43 @@ class _OpenSSLVersionInfo(typing.NamedTuple):
|
||||||
class OpenSSLVersionsDirectory(KeyedList[_OpenSSLVersionInfo, packaging.version.Version]):
|
class OpenSSLVersionsDirectory(KeyedList[_OpenSSLVersionInfo, packaging.version.Version]):
|
||||||
key_field = "version"
|
key_field = "version"
|
||||||
|
|
||||||
def get_store_latest_release(self) \
|
def get_store_latest_release(self, major: int) \
|
||||||
-> _OpenSSLVersionInfo:
|
-> _OpenSSLVersionInfo:
|
||||||
if self:
|
matching = [
|
||||||
#already retrieved
|
release for release in self
|
||||||
return self[max(self.keys())]
|
if release.version.major == major
|
||||||
|
]
|
||||||
|
if matching:
|
||||||
|
return max(matching, key=lambda release: release.version)
|
||||||
|
|
||||||
|
url = "https://api.github.com/repos/openssl/openssl/releases"
|
||||||
|
while url:
|
||||||
|
response = Requests.get(url)
|
||||||
|
matching = [
|
||||||
|
release
|
||||||
|
for release in response.json()
|
||||||
|
if not release['draft']
|
||||||
|
and not release['prerelease']
|
||||||
|
and (match := OPENSSL_RELEASE_TAG_RE.fullmatch(
|
||||||
|
release['tag_name']
|
||||||
|
))
|
||||||
|
and int(match.group('major')) == major
|
||||||
|
]
|
||||||
|
if matching:
|
||||||
|
j_release = matching[0]
|
||||||
|
break
|
||||||
|
url = response.links.get('next', {}).get('url')
|
||||||
|
else:
|
||||||
|
raise ValueError(f"No OpenSSL {major}.x release found")
|
||||||
|
|
||||||
j = requests.get("https://api.github.com/repos/openssl/openssl/releases/latest", timeout=30).json()
|
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
# urlparse can parse str as well as bytes
|
# urlparse can parse str as well as bytes
|
||||||
shasum_url = more_itertools.one(
|
shasum_url = more_itertools.one(
|
||||||
asset['browser_download_url']
|
asset['browser_download_url']
|
||||||
for asset in j['assets']
|
for asset in j_release['assets']
|
||||||
if urllib.parse.urlparse(asset['browser_download_url']).path.split('/')[-1].endswith('.sha256')
|
if urllib.parse.urlparse(asset['browser_download_url']).path.split('/')[-1].endswith('.sha256')
|
||||||
)
|
)
|
||||||
shasum_text = requests.get(shasum_url, timeout=30).text
|
shasum_text = Requests.get(shasum_url).text
|
||||||
shasum_data = jc.parse("hashsum", shasum_text, quiet=True)[0]
|
shasum_data = jc.parse("hashsum", shasum_text, quiet=True)[0]
|
||||||
package_hash, package_filename = shasum_data["hash"], shasum_data["filename"]
|
package_hash, package_filename = shasum_data["hash"], shasum_data["filename"]
|
||||||
del shasum_data, shasum_text, shasum_url
|
del shasum_data, shasum_text, shasum_url
|
||||||
|
|
@ -467,7 +580,7 @@ class OpenSSLVersionsDirectory(KeyedList[_OpenSSLVersionInfo, packaging.version.
|
||||||
|
|
||||||
package_url = more_itertools.one(
|
package_url = more_itertools.one(
|
||||||
asset['browser_download_url']
|
asset['browser_download_url']
|
||||||
for asset in j['assets']
|
for asset in j_release['assets']
|
||||||
if urllib.parse.urlparse(asset['browser_download_url']).path.split('/')[-1] == package_filename
|
if urllib.parse.urlparse(asset['browser_download_url']).path.split('/')[-1] == package_filename
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -550,6 +663,7 @@ class DownloadPage:
|
||||||
if session is None:
|
if session is None:
|
||||||
session = requests_html.HTMLSession()
|
session = requests_html.HTMLSession()
|
||||||
response = session.get(url, timeout=30)
|
response = session.get(url, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
page = response.html
|
page = response.html
|
||||||
table = page.find("pre", first=True)
|
table = page.find("pre", first=True)
|
||||||
# some GNU mirrors format entries as a table
|
# some GNU mirrors format entries as a table
|
||||||
|
|
@ -627,6 +741,18 @@ class Url:
|
||||||
h.update(c)
|
h.update(c)
|
||||||
return h.hexdigest()
|
return h.hexdigest()
|
||||||
|
|
||||||
|
class Requests:
|
||||||
|
@staticmethod
|
||||||
|
def get(url: str) -> requests.Response:
|
||||||
|
response = requests.get(url, timeout=30)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main())
|
#sys.excepthook seems to have no effect in Github Actions
|
||||||
|
try:
|
||||||
|
sys.exit(main())
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Unhandled exception occurred")
|
||||||
|
sys.exit(2)
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,10 @@ class CondaVersion(NamedTuple):
|
||||||
# since 4.8, Miniconda specifies versions explicitly in the file name
|
# since 4.8, Miniconda specifies versions explicitly in the file name
|
||||||
raise ValueError("Miniconda 4.8+ is supposed to specify a Python version explicitly")
|
raise ValueError("Miniconda 4.8+ is supposed to specify a Python version explicitly")
|
||||||
if self.flavor == "anaconda":
|
if self.flavor == "anaconda":
|
||||||
# https://www.anaconda.com/docs/tools/anaconda-org/release-notes
|
# Info about the bundled Python version
|
||||||
|
# https://www.anaconda.com/docs/getting-started/anaconda/release-notes
|
||||||
|
if v >= (2026,7):
|
||||||
|
return PyVersion.PY314
|
||||||
if v >= (2025,6):
|
if v >= (2025,6):
|
||||||
return PyVersion.PY313
|
return PyVersion.PY313
|
||||||
if v >= (2024,6):
|
if v >= (2024,6):
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
'Adds the latest miniforge and mambaforge releases.'
|
'Adds the latest miniforge and mambaforge releases.'
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
|
|
@ -8,7 +9,7 @@ import string
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=os.environ.get('LOGLEVEL', 'INFO'))
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
MINIFORGE_REPO = 'conda-forge/miniforge'
|
MINIFORGE_REPO = 'conda-forge/miniforge'
|
||||||
DISTRIBUTIONS = ['miniforge']
|
DISTRIBUTIONS = ['miniforge']
|
||||||
|
|
@ -21,6 +22,8 @@ SKIPPED_RELEASES = [
|
||||||
'22.11.1-2', #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
|
'22.11.1-2', #MacOS packages are broken (have broken dep tarballs, downloading them fails with 403)
|
||||||
'25.3.0-0', #marked as prerelease, no Linux version
|
'25.3.0-0', #marked as prerelease, no Linux version
|
||||||
'25.11.0-0', #regression reported in constructor, re-released as 25.11.0-1 with hotfix bumping to constructor>=3.14 (was >=3.12, 3.13 implicit)
|
'25.11.0-0', #regression reported in constructor, re-released as 25.11.0-1 with hotfix bumping to constructor>=3.14 (was >=3.12, 3.13 implicit)
|
||||||
|
'26.1.1-0', #prerelease, no binary assets
|
||||||
|
'26.1.1-1', #prerelease, no binary assets
|
||||||
]
|
]
|
||||||
|
|
||||||
install_script_fmt = """
|
install_script_fmt = """
|
||||||
|
|
@ -136,7 +139,7 @@ def main():
|
||||||
if version in SKIPPED_RELEASES:
|
if version in SKIPPED_RELEASES:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
logger.info(f'Looking for {version} in {out_dir}')
|
logger.debug(f'Looking for {version} in {out_dir}')
|
||||||
|
|
||||||
# mambaforge is retired https://github.com/conda-forge/miniforge/releases/tag/24.11.2-0
|
# mambaforge is retired https://github.com/conda-forge/miniforge/releases/tag/24.11.2-0
|
||||||
if version_tuple(version) >= (24, 11, 2):
|
if version_tuple(version) >= (24, 11, 2):
|
||||||
|
|
@ -149,4 +152,17 @@ def main():
|
||||||
add_version(release, distributions)
|
add_version(release, distributions)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument(
|
||||||
|
"-d", "--dry-run", action="store_true",
|
||||||
|
help="Do not write scripts, just report them to stdout",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-v", "--verbose", action="store_true", default=0,
|
||||||
|
help="Increase verbosity of logging",
|
||||||
|
)
|
||||||
|
parsed = parser.parse_args()
|
||||||
|
|
||||||
|
if parsed.verbose: logging.getLogger.setLevel(logging.DEBUG)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
more_itertools
|
more_itertools
|
||||||
requests-html
|
requests-html
|
||||||
fake_useragent<2
|
fake_useragent<2; python_version < "3.9"
|
||||||
lxml[html_clean]
|
lxml[html_clean]
|
||||||
packaging
|
packaging
|
||||||
requests
|
requests
|
||||||
sortedcontainers
|
sortedcontainers
|
||||||
tqdm
|
tqdm
|
||||||
jc @ git+https://github.com/native-api/jc@haslib_mode
|
jc
|
||||||
|
|
|
||||||
9
plugins/python-build/share/python-build/3.10.21
Normal file
9
plugins/python-build/share/python-build/3.10.21
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
prefer_openssl3
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
install_package "openssl-3.6.3" "https://github.com/openssl/openssl/releases/download/openssl-3.6.3/openssl-3.6.3.tar.gz#243a86649cf6f23eeb6a2ff2456e09e5d77dd9018a54d3d96b0c6bdd6ba6c7f1" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.10.21" "https://www.python.org/ftp/python/3.10.21/Python-3.10.21.tar.xz#a0da1e72132e950154eca0f6f47d5db828454700de20e5113667940d81e0db04" standard verify_py310 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.10.21" "https://www.python.org/ftp/python/3.10.21/Python-3.10.21.tgz#f276987f06270ae6c1fb4da620bd105edf78c31368c2f7e85e6c1d51c560b04b" standard verify_py310 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
11
plugins/python-build/share/python-build/3.11.16
Normal file
11
plugins/python-build/share/python-build/3.11.16
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefer_openssl3
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
||||||
|
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
||||||
|
install_package "openssl-3.6.3" "https://github.com/openssl/openssl/releases/download/openssl-3.6.3/openssl-3.6.3.tar.gz#243a86649cf6f23eeb6a2ff2456e09e5d77dd9018a54d3d96b0c6bdd6ba6c7f1" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.11.16" "https://www.python.org/ftp/python/3.11.16/Python-3.11.16.tar.xz#91bcdebfdde239a003ae93738a7fce0f9230fee5c4bc2b86f6e6e8c6f98aabe8" standard verify_py311 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.11.16" "https://www.python.org/ftp/python/3.11.16/Python-3.11.16.tgz#6c0bd76ab0ec7d94ed400b1497f01ac6c7751c8822615ee0855a3eb2d893ea76" standard verify_py311 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
11
plugins/python-build/share/python-build/3.12.14
Normal file
11
plugins/python-build/share/python-build/3.12.14
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefer_openssl3
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
||||||
|
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
||||||
|
install_package "openssl-3.6.3" "https://github.com/openssl/openssl/releases/download/openssl-3.6.3/openssl-3.6.3.tar.gz#243a86649cf6f23eeb6a2ff2456e09e5d77dd9018a54d3d96b0c6bdd6ba6c7f1" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.12.14" "https://www.python.org/ftp/python/3.12.14/Python-3.12.14.tar.xz#5c8462af5790baf43a321a1559dbe0db06d1be4300fb85fb53c40060668e548a" standard verify_py312 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.12.14" "https://www.python.org/ftp/python/3.12.14/Python-3.12.14.tgz#6c6df908d2c3fd24e6d76869e92542abd0f33aec9dfc18df8875f89660286d43" standard verify_py312 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
11
plugins/python-build/share/python-build/3.13.14
Normal file
11
plugins/python-build/share/python-build/3.13.14
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefer_openssl3
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
||||||
|
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
||||||
|
install_package "openssl-3.6.3" "https://github.com/openssl/openssl/releases/download/openssl-3.6.3/openssl-3.6.3.tar.gz#243a86649cf6f23eeb6a2ff2456e09e5d77dd9018a54d3d96b0c6bdd6ba6c7f1" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.13.14" "https://www.python.org/ftp/python/3.13.14/Python-3.13.14.tar.xz#639e43243c620a308f968213df9e00f2f8f62332f7adbaa7a7eeb9783057c690" standard verify_py313 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.13.14" "https://www.python.org/ftp/python/3.13.14/Python-3.13.14.tgz#5ae535a36af0ebca6fca176ecb8197f5db9c1cb8c8f0cd12cdf1787046db1f41" standard verify_py313 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
11
plugins/python-build/share/python-build/3.13.15
Normal file
11
plugins/python-build/share/python-build/3.13.15
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefer_openssl3
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
||||||
|
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
||||||
|
install_package "openssl-3.6.3" "https://github.com/openssl/openssl/releases/download/openssl-3.6.3/openssl-3.6.3.tar.gz#243a86649cf6f23eeb6a2ff2456e09e5d77dd9018a54d3d96b0c6bdd6ba6c7f1" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.13.15" "https://www.python.org/ftp/python/3.13.15/Python-3.13.15.tar.xz#1e66a7945a48390ee4c2a4268a0e4185884059a13c4aab6d148aa208deea4a76" standard verify_py313 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.13.15" "https://www.python.org/ftp/python/3.13.15/Python-3.13.15.tgz#c28d9d213c09b5b5ab2c29812950e12f746999e099b82894231be954b26baed9" standard verify_py313 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
2
plugins/python-build/share/python-build/3.13.15t
Normal file
2
plugins/python-build/share/python-build/3.13.15t
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export PYTHON_BUILD_FREE_THREADING=1
|
||||||
|
source "${BASH_SOURCE[0]%t}"
|
||||||
11
plugins/python-build/share/python-build/3.14.6
Normal file
11
plugins/python-build/share/python-build/3.14.6
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefer_openssl3_to_4
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
||||||
|
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
||||||
|
install_package "openssl-4.0.1" "https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz#2db3f3a0d6ea4b59e1f094ace2c8cd536dffb87cdc39084c5afa1e6f7f37dd09" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.14.6" "https://www.python.org/ftp/python/3.14.6/Python-3.14.6.tar.xz#143b1dddefaec3bd2e21e3b839b34a2b7fb9842272883c576420d605e9f30c63" standard verify_py314 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.14.6" "https://www.python.org/ftp/python/3.14.6/Python-3.14.6.tgz#74d0d71d0600e477651a077101d6e62d1e2e69b8e992ba18c993dd643b7ba222" standard verify_py314 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
2
plugins/python-build/share/python-build/3.14.6t
Normal file
2
plugins/python-build/share/python-build/3.14.6t
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export PYTHON_BUILD_FREE_THREADING=1
|
||||||
|
source "${BASH_SOURCE[0]%t}"
|
||||||
11
plugins/python-build/share/python-build/3.14.7
Normal file
11
plugins/python-build/share/python-build/3.14.7
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefer_openssl3_to_4
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
||||||
|
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
||||||
|
install_package "openssl-4.0.1" "https://github.com/openssl/openssl/releases/download/openssl-4.0.1/openssl-4.0.1.tar.gz#2db3f3a0d6ea4b59e1f094ace2c8cd536dffb87cdc39084c5afa1e6f7f37dd09" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.14.7" "https://www.python.org/ftp/python/3.14.7/Python-3.14.7.tar.xz#3b48dac8fb59f62eaa67ac83c1eb12bda1b7a08406dd286e252c11a66be27f81" standard verify_py314 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.14.7" "https://www.python.org/ftp/python/3.14.7/Python-3.14.7.tgz#62859805f6fdf25e2bcbf3fa3217801e1996887ca33e6a2af80674bdfa2dbe07" standard verify_py314 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
2
plugins/python-build/share/python-build/3.14.7t
Normal file
2
plugins/python-build/share/python-build/3.14.7t
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export PYTHON_BUILD_FREE_THREADING=1
|
||||||
|
source "${BASH_SOURCE[0]%t}"
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
prefer_openssl3
|
|
||||||
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
|
||||||
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
|
||||||
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
|
||||||
install_package "openssl-4.0.0" "https://github.com/openssl/openssl/releases/download/openssl-4.0.0/openssl-4.0.0.tar.gz#c32cf49a959c4f345f9606982dd36e7d28f7c58b19c2e25d75624d2b3d2f79ac" mac_openssl --if has_broken_mac_openssl
|
|
||||||
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
|
||||||
if has_tar_xz_support; then
|
|
||||||
install_package "Python-3.15.0b2" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0b2.tar.xz#d14f474ab679e90bc734b02ff58447b6ec99a821af61d6ff0c1da0f86e341a71" standard verify_py315 copy_python_gdb ensurepip
|
|
||||||
else
|
|
||||||
install_package "Python-3.15.0b2" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0b2.tgz#781f4bcdef48d1d38b335fdc7a156b4e5fe9738b14456121f949257ff5cce77c" standard verify_py315 copy_python_gdb ensurepip
|
|
||||||
fi
|
|
||||||
11
plugins/python-build/share/python-build/3.15.0rc2
Normal file
11
plugins/python-build/share/python-build/3.15.0rc2
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
prefer_openssl3
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL=1
|
||||||
|
export PYTHON_BUILD_CONFIGURE_WITH_OPENSSL_RPATH=1
|
||||||
|
export PYTHON_BUILD_TCLTK_USE_PKGCONFIG=1
|
||||||
|
install_package "openssl-4.0.2" "https://github.com/openssl/openssl/releases/download/openssl-4.0.2/openssl-4.0.2.tar.gz#736b467530f916737b7031310ccb21d8218c6229e61e8e160cd1d3458cd543a8" mac_openssl --if has_broken_mac_openssl
|
||||||
|
install_package "readline-8.3" "https://ftpmirror.gnu.org/readline/readline-8.3.tar.gz#fe5383204467828cd495ee8d1d3c037a7eba1389c22bc6a041f627976f9061cc" mac_readline --if has_broken_mac_readline
|
||||||
|
if has_tar_xz_support; then
|
||||||
|
install_package "Python-3.15.0rc2" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0rc2.tar.xz#8d93af5eaaaea5adfd41bd786a7ba3f03f2ad1ab57c6a65e0b963deab91d5ad7" standard verify_py315 copy_python_gdb ensurepip
|
||||||
|
else
|
||||||
|
install_package "Python-3.15.0rc2" "https://www.python.org/ftp/python/3.15.0/Python-3.15.0rc2.tgz#164dbb80f5e1fdd8768387724bfa014da1000401b2015d872a2396c8b858b829" standard verify_py315 copy_python_gdb ensurepip
|
||||||
|
fi
|
||||||
2
plugins/python-build/share/python-build/3.15.0rc2t
Normal file
2
plugins/python-build/share/python-build/3.15.0rc2t
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
export PYTHON_BUILD_FREE_THREADING=1
|
||||||
|
source "${BASH_SOURCE[0]%t}"
|
||||||
|
|
@ -2,7 +2,7 @@ prefer_openssl11
|
||||||
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
||||||
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
||||||
if has_tar_xz_support; then
|
if has_tar_xz_support; then
|
||||||
install_package "Python-3.6.10" "https://www.python.org/ftp/python/3.6.10/Python-3.6.10.tar.xz#0a833c398ac8cd7c5538f7232d8531afef943c60495c504484f308dac3af40de" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.10" "https://www.python.org/ftp/python/3.6.10/Python-3.6.10.tar.xz#0a833c398ac8cd7c5538f7232d8531afef943c60495c504484f308dac3af40de" standard verify_py36 copy_python_gdb ensurepip
|
||||||
else
|
else
|
||||||
install_package "Python-3.6.10" "https://www.python.org/ftp/python/3.6.10/Python-3.6.10.tgz#7034dd7cba98d4f94c74f9edd7345bac71c8814c41672c64d9044fa2f96f334d" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.10" "https://www.python.org/ftp/python/3.6.10/Python-3.6.10.tgz#7034dd7cba98d4f94c74f9edd7345bac71c8814c41672c64d9044fa2f96f334d" standard verify_py36 copy_python_gdb ensurepip
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ prefer_openssl11
|
||||||
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
||||||
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
||||||
if has_tar_xz_support; then
|
if has_tar_xz_support; then
|
||||||
install_package "Python-3.6.11" "https://www.python.org/ftp/python/3.6.11/Python-3.6.11.tar.xz#741ebdcbc4e3937a5ff23517dd455ebf7d543ea9fef6f5cf6f46e575d6c4fda4" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.11" "https://www.python.org/ftp/python/3.6.11/Python-3.6.11.tar.xz#741ebdcbc4e3937a5ff23517dd455ebf7d543ea9fef6f5cf6f46e575d6c4fda4" standard verify_py36 copy_python_gdb ensurepip
|
||||||
else
|
else
|
||||||
install_package "Python-3.6.11" "https://www.python.org/ftp/python/3.6.11/Python-3.6.11.tgz#96621902f89746fffc22f39749c07da7c2917b232e72352e6837d41850f7b90c" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.11" "https://www.python.org/ftp/python/3.6.11/Python-3.6.11.tgz#96621902f89746fffc22f39749c07da7c2917b232e72352e6837d41850f7b90c" standard verify_py36 copy_python_gdb ensurepip
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ prefer_openssl11
|
||||||
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
||||||
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
||||||
if has_tar_xz_support; then
|
if has_tar_xz_support; then
|
||||||
install_package "Python-3.6.12" "https://www.python.org/ftp/python/3.6.12/Python-3.6.12.tar.xz#70953a9b5d6891d92e65d184c3512126a15814bee15e1eff2ddcce04334e9a99" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.12" "https://www.python.org/ftp/python/3.6.12/Python-3.6.12.tar.xz#70953a9b5d6891d92e65d184c3512126a15814bee15e1eff2ddcce04334e9a99" standard verify_py36 copy_python_gdb ensurepip
|
||||||
else
|
else
|
||||||
install_package "Python-3.6.12" "https://www.python.org/ftp/python/3.6.12/Python-3.6.12.tgz#12dddbe52385a0f702fb8071e12dcc6b3cb2dde07cd8db3ed60e90d90ab78693" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.12" "https://www.python.org/ftp/python/3.6.12/Python-3.6.12.tgz#12dddbe52385a0f702fb8071e12dcc6b3cb2dde07cd8db3ed60e90d90ab78693" standard verify_py36 copy_python_gdb ensurepip
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ prefer_openssl11
|
||||||
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
||||||
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
||||||
if has_tar_xz_support; then
|
if has_tar_xz_support; then
|
||||||
install_package "Python-3.6.13" "https://www.python.org/ftp/python/3.6.13/Python-3.6.13.tar.xz#a47a43a53abb42286a2c11965343ff56711b9e64e8d11bf2c6701a4fb8ce1a0f" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.13" "https://www.python.org/ftp/python/3.6.13/Python-3.6.13.tar.xz#a47a43a53abb42286a2c11965343ff56711b9e64e8d11bf2c6701a4fb8ce1a0f" standard verify_py36 copy_python_gdb ensurepip
|
||||||
else
|
else
|
||||||
install_package "Python-3.6.13" "https://www.python.org/ftp/python/3.6.13/Python-3.6.13.tgz#614950d3d54f6e78dac651b49c64cfe2ceefea5af3aff3371a9e4b27a53b2669" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.13" "https://www.python.org/ftp/python/3.6.13/Python-3.6.13.tgz#614950d3d54f6e78dac651b49c64cfe2ceefea5af3aff3371a9e4b27a53b2669" standard verify_py36 copy_python_gdb ensurepip
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ prefer_openssl11
|
||||||
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
||||||
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
||||||
if has_tar_xz_support; then
|
if has_tar_xz_support; then
|
||||||
install_package "Python-3.6.7" "https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tar.xz#81fd1401a9d66533b0a3e9e3f4ea1c7c6702d57d5b90d659f971e6f1b745f77d" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.7" "https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tar.xz#81fd1401a9d66533b0a3e9e3f4ea1c7c6702d57d5b90d659f971e6f1b745f77d" standard verify_py36 copy_python_gdb ensurepip
|
||||||
else
|
else
|
||||||
install_package "Python-3.6.7" "https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tgz#b7c36f7ed8f7143b2c46153b7332db2227669f583ea0cce753facf549d1a4239" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.7" "https://www.python.org/ftp/python/3.6.7/Python-3.6.7.tgz#b7c36f7ed8f7143b2c46153b7332db2227669f583ea0cce753facf549d1a4239" standard verify_py36 copy_python_gdb ensurepip
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ prefer_openssl11
|
||||||
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
||||||
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
||||||
if has_tar_xz_support; then
|
if has_tar_xz_support; then
|
||||||
install_package "Python-3.6.8" "https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tar.xz#35446241e995773b1bed7d196f4b624dadcadc8429f26282e756b2fb8a351193" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.8" "https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tar.xz#35446241e995773b1bed7d196f4b624dadcadc8429f26282e756b2fb8a351193" standard verify_py36 copy_python_gdb ensurepip
|
||||||
else
|
else
|
||||||
install_package "Python-3.6.8" "https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tgz#7f5b1f08b3b0a595387ef6c64c85b1b13b38abef0dd871835ee923262e4f32f0" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.8" "https://www.python.org/ftp/python/3.6.8/Python-3.6.8.tgz#7f5b1f08b3b0a595387ef6c64c85b1b13b38abef0dd871835ee923262e4f32f0" standard verify_py36 copy_python_gdb ensurepip
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ prefer_openssl11
|
||||||
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
install_package "openssl-1.1.0j" "https://www.openssl.org/source/old/1.1.0/openssl-1.1.0j.tar.gz#31bec6c203ce1a8e93d5994f4ed304c63ccf07676118b6634edded12ad1b3246" mac_openssl --if has_broken_mac_openssl
|
||||||
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
|
||||||
if has_tar_xz_support; then
|
if has_tar_xz_support; then
|
||||||
install_package "Python-3.6.9" "https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tar.xz#5e2f5f554e3f8f7f0296f7e73d8600c4e9acbaee6b2555b83206edf5153870da" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.9" "https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tar.xz#5e2f5f554e3f8f7f0296f7e73d8600c4e9acbaee6b2555b83206edf5153870da" standard verify_py36 copy_python_gdb ensurepip
|
||||||
else
|
else
|
||||||
install_package "Python-3.6.9" "https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tgz#47fc92a1dcb946b9ed0abc311d3767b7215c54e655b17fd1d3f9b538195525aa" standard verify_py37 copy_python_gdb ensurepip
|
install_package "Python-3.6.9" "https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tgz#47fc92a1dcb946b9ed0abc311d3767b7215c54e655b17fd1d3f9b538195525aa" standard verify_py36 copy_python_gdb ensurepip
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
19
plugins/python-build/share/python-build/anaconda3-2026.07-1
Normal file
19
plugins/python-build/share/python-build/anaconda3-2026.07-1
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Anaconda3-2026.07-1-Linux-aarch64" "https://repo.anaconda.com/archive/Anaconda3-2026.07-1-Linux-aarch64.sh#af2fd0025728a04408e0481d5668b92c53589cbfc66525a15119e6503742a04d" "anaconda" verify_py314
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Anaconda3-2026.07-1-Linux-x86_64" "https://repo.anaconda.com/archive/Anaconda3-2026.07-1-Linux-x86_64.sh#9f49042048e9a8220c1cddf9192054290724052d3b2a86f938f9f5edd911db8f" "anaconda" verify_py314
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Anaconda3-2026.07-1-MacOSX-arm64" "https://repo.anaconda.com/archive/Anaconda3-2026.07-1-MacOSX-arm64.sh#6a9b63c645db4dbe293ffe9f168f7e380fe7a041fc863e67ee84db027425bce2" "anaconda" verify_py314
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Anaconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
61
plugins/python-build/share/python-build/graalpy3.12-25.1.3
Normal file
61
plugins/python-build/share/python-build/graalpy3.12-25.1.3
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
# this software and associated documentation files (the "Software"), to deal in
|
||||||
|
# the Software without restriction, including without limitation the rights to
|
||||||
|
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
# so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
VERSION='25.1.3'
|
||||||
|
BUILD=''
|
||||||
|
|
||||||
|
colorize 1 "GraalPy 23.1 and later installed by python-build use the faster Oracle GraalVM distribution" && echo
|
||||||
|
colorize 1 "Oracle GraalVM uses the GFTC license, which is free for development and production use, see https://medium.com/graalvm/161527df3d76" && echo
|
||||||
|
colorize 1 "The GraalVM Community Edition variant of GraalPy is also available, under the name graalpy3.12-community-${VERSION}" && echo
|
||||||
|
|
||||||
|
|
||||||
|
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
|
||||||
|
|
||||||
|
case "$graalpy_arch" in
|
||||||
|
"linux-amd64" )
|
||||||
|
checksum="53d46f3ad78229699eb00677f4cb5e3cfe451b58c46970e4d4272a719b53ce82"
|
||||||
|
;;
|
||||||
|
"linux-aarch64" )
|
||||||
|
checksum="947170f0896c8acb02b364caad3e0c01439c12cbdad1b7605cff589b7dd5b26e"
|
||||||
|
;;
|
||||||
|
"macos-aarch64" )
|
||||||
|
checksum="adb17eb3aa3c5701cfc518af26bbaf4e5755ecb62a6a22d490af06523ea02913"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "${BUILD}" ]; then
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo "Oracle GraalPy currently doesn't provide snapshot builds. Use graalpy3.12-community if you need snapshots."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
url="https://github.com/oracle/graalpython/releases/download/graal-${VERSION}/graalpy3.12-${VERSION}-${graalpy_arch}.tar.gz#${checksum}"
|
||||||
|
|
||||||
|
install_package "graalpy3.12-${VERSION}" "${url}" "copy" ensurepip
|
||||||
49
plugins/python-build/share/python-build/graalpy3.12-25.2.4
Normal file
49
plugins/python-build/share/python-build/graalpy3.12-25.2.4
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
# this software and associated documentation files (the "Software"), to deal in
|
||||||
|
# the Software without restriction, including without limitation the rights to
|
||||||
|
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
# so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
VERSION='25.2.4'
|
||||||
|
|
||||||
|
colorize 1 "GraalPy 23.1 and later installed by python-build use the faster Oracle GraalVM distribution" && echo
|
||||||
|
colorize 1 "Oracle GraalVM uses the GFTC license, which is free for development and production use, see https://medium.com/graalvm/161527df3d76" && echo
|
||||||
|
colorize 1 "The GraalVM Community Edition variant of GraalPy is also available, under the name graalpy3.12-community-${VERSION}" && echo
|
||||||
|
|
||||||
|
|
||||||
|
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
|
||||||
|
|
||||||
|
case "$graalpy_arch" in
|
||||||
|
"linux-amd64" )
|
||||||
|
checksum="b3d0766ae6d55daa15f0db1f6c884383d7eec506b186d0ba2f702523a78ff28d"
|
||||||
|
;;
|
||||||
|
"linux-aarch64" )
|
||||||
|
checksum="e57472272b1b659ae6ac972117723b0515a49cc9577688ed5563919793170d67"
|
||||||
|
;;
|
||||||
|
"macos-aarch64" )
|
||||||
|
checksum="79f0e1fefb42bb484122ae9e50cb21ce14541d18e6bd66c498e44b459ce652df"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
install_package "graalpy3.12-${VERSION}" "https://github.com/oracle/graalpython/releases/download/graal-${VERSION}/graalpy3.12-${VERSION}-${graalpy_arch}.tar.gz#${checksum}" "copy" ensurepip
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
# this software and associated documentation files (the "Software"), to deal in
|
||||||
|
# the Software without restriction, including without limitation the rights to
|
||||||
|
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
# so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
VERSION='25.1.3'
|
||||||
|
BUILD=''
|
||||||
|
|
||||||
|
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
|
||||||
|
|
||||||
|
case "$graalpy_arch" in
|
||||||
|
"linux-amd64" )
|
||||||
|
checksum="1705b59e5a3d04364b1fa4cbb31ad6c273b487a87dc67e336fc5476b599c8d3e"
|
||||||
|
;;
|
||||||
|
"linux-aarch64" )
|
||||||
|
checksum="2558dec612e02b1ce5d7807fdd338f2a65dd9a0e62913458a32202289cc211ac"
|
||||||
|
;;
|
||||||
|
"macos-aarch64" )
|
||||||
|
checksum="76c0ccde939b94e6669a0e7eb01df612a070a6dbb966ed70989a851a9b1066e8"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ -n "${BUILD}" ]; then
|
||||||
|
url="https://github.com/graalvm/graalvm-ce-dev-builds/releases/download/${VERSION}-dev-${BUILD}/graalpy3.12-community-dev-${graalpy_arch}.tar.gz"
|
||||||
|
else
|
||||||
|
url="https://github.com/oracle/graalpython/releases/download/graal-${VERSION}/graalpy3.12-community-${VERSION}-${graalpy_arch}.tar.gz#${checksum}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_package "graalpy3.12-community-${VERSION}${BUILD}" "${url}" "copy" ensurepip
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
# this software and associated documentation files (the "Software"), to deal in
|
||||||
|
# the Software without restriction, including without limitation the rights to
|
||||||
|
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
# so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
VERSION='25.2.4'
|
||||||
|
|
||||||
|
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
|
||||||
|
|
||||||
|
case "$graalpy_arch" in
|
||||||
|
"linux-amd64" )
|
||||||
|
checksum="98af303a6b714bde39077354733254f78023ddef06d61e14cb66a3a01f10ae82"
|
||||||
|
;;
|
||||||
|
"linux-aarch64" )
|
||||||
|
checksum="dda5e97c8b38158184dec189addaece57a37207a0dad38be162621d605f964f5"
|
||||||
|
;;
|
||||||
|
"macos-aarch64" )
|
||||||
|
checksum="76a182713660ce69f640b9666c2861ec8a5839637a972eaa80c6b5cf38f1357b"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
install_package "graalpy3.12-community-${VERSION}" "https://github.com/oracle/graalpython/releases/download/graal-${VERSION}/graalpy3.12-community-${VERSION}-${graalpy_arch}.tar.gz#${checksum}" "copy" ensurepip
|
||||||
50
plugins/python-build/share/python-build/graalpy3.13-25.3.4.1
Normal file
50
plugins/python-build/share/python-build/graalpy3.13-25.3.4.1
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
# this software and associated documentation files (the "Software"), to deal in
|
||||||
|
# the Software without restriction, including without limitation the rights to
|
||||||
|
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
# so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
VERSION='25.3.4.1'
|
||||||
|
RELEASE_TAG='graal-25.3.4'
|
||||||
|
|
||||||
|
colorize 1 "GraalPy 23.1 and later installed by python-build use the faster Oracle GraalVM distribution" && echo
|
||||||
|
colorize 1 "Oracle GraalVM uses the GFTC license, which is free for development and production use, see https://medium.com/graalvm/161527df3d76" && echo
|
||||||
|
colorize 1 "The GraalVM Community Edition variant of GraalPy is also available, under the name graalpy3.13-community-${VERSION}" && echo
|
||||||
|
|
||||||
|
|
||||||
|
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
|
||||||
|
|
||||||
|
case "$graalpy_arch" in
|
||||||
|
"linux-amd64" )
|
||||||
|
checksum="23515b4e659d645960517b0ceedb90faa84fcb01f0f4497beca5868a1b1ae3ea"
|
||||||
|
;;
|
||||||
|
"linux-aarch64" )
|
||||||
|
checksum="53b80e284e444540ae7cb67349af3dd8f6c1848fbf6abb5051742ac745f1f33d"
|
||||||
|
;;
|
||||||
|
"macos-aarch64" )
|
||||||
|
checksum="8ac33b5a5d0ee872751595211508fd826f47051ff6e6614032aed1de071eebab"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
install_package "graalpy3.13-${VERSION}" "https://github.com/oracle/graalpython/releases/download/${RELEASE_TAG}/graalpy3.13-${VERSION}-${graalpy_arch}.tar.gz#${checksum}" "copy" ensurepip
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
# this software and associated documentation files (the "Software"), to deal in
|
||||||
|
# the Software without restriction, including without limitation the rights to
|
||||||
|
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||||
|
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||||
|
# so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
VERSION='25.3.4.1'
|
||||||
|
RELEASE_TAG='graal-25.3.4'
|
||||||
|
|
||||||
|
graalpy_arch="$(graalpy_architecture 2>/dev/null || true)"
|
||||||
|
|
||||||
|
case "$graalpy_arch" in
|
||||||
|
"linux-amd64" )
|
||||||
|
checksum="5e239616c8352488585f58d33991d262e8c627483741778ad43d39a4830176ad"
|
||||||
|
;;
|
||||||
|
"linux-aarch64" )
|
||||||
|
checksum="13007ecfe8180e91f1060de60a2ba6ce88f38faa4a36574b34143a492e8c660e"
|
||||||
|
;;
|
||||||
|
"macos-aarch64" )
|
||||||
|
checksum="e2cf7ae230478ad8d100ad868f75e3addb174d81e7f550322ea5e74cab066644"
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": No binary distribution of GraalPy is available for $(uname -sm)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
install_package "graalpy3.13-community-${VERSION}" "https://github.com/oracle/graalpython/releases/download/${RELEASE_TAG}/graalpy3.13-community-${VERSION}-${graalpy_arch}.tar.gz#${checksum}" "copy" ensurepip
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py310_26.5.3-1-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py310_26.5.3-1-Linux-aarch64.sh#d2988632a30ea71e158874b9c5386d016660908fec38ee58125af7783b1a0bf6" "miniconda" verify_py310
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py310_26.5.3-1-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py310_26.5.3-1-Linux-x86_64.sh#4a82fe0a50a28e8a9406b3ed8e465b7009aa7d0225566802c3370df96b10d834" "miniconda" verify_py310
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py310_26.5.3-1-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py310_26.5.3-1-MacOSX-arm64.sh#7868e87c7e1ecaa6ff25f5041e0abdf766e5e2d082788b26bcf57554d0036a2d" "miniconda" verify_py310
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py310_26.5.3-2-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py310_26.5.3-2-Linux-aarch64.sh#69b30d25b1308596e39819d2eeb8d669fec8b35205254d4d38975e9a6eb525e5" "miniconda" verify_py310
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py310_26.5.3-2-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py310_26.5.3-2-Linux-x86_64.sh#83e53ab0246ca320db28734bfd16f9bba895d96ae3e0ccf6f9b65e1383ab6a26" "miniconda" verify_py310
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py310_26.5.3-2-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py310_26.5.3-2-MacOSX-arm64.sh#fc25d37469e162e3073093c1e02a0a21a59681af10a097f73df3f303b89b050a" "miniconda" verify_py310
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py311_26.5.3-1-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py311_26.5.3-1-Linux-aarch64.sh#4a2e39642fcd0b25cf199f5ada4725120a9a40fa10e864d67b8f93e7f2f44dad" "miniconda" verify_py311
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py311_26.5.3-1-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py311_26.5.3-1-Linux-x86_64.sh#f1d308a450763ce617f4e4f1609521358f663b2d1c097cfcc11a1c1f09baf680" "miniconda" verify_py311
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py311_26.5.3-1-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py311_26.5.3-1-MacOSX-arm64.sh#721bccd83c53ff56e364faf883c5da9abfdf61daecb4863c533818851b33edc3" "miniconda" verify_py311
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py311_26.5.3-2-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py311_26.5.3-2-Linux-aarch64.sh#e50d1c902e94b6f64a19e0a3fbc35f8cadc09c5a1cf5b0fe10da14e4ce7ff24d" "miniconda" verify_py311
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py311_26.5.3-2-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py311_26.5.3-2-Linux-x86_64.sh#cdca3dd8440759bb87c60b227e26946263fe2856b30b2bcfdd964c38254fb8eb" "miniconda" verify_py311
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py311_26.5.3-2-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py311_26.5.3-2-MacOSX-arm64.sh#129bee9cb8e23d0eed175439a8b41c9844bac5c75f3f2aefa0be7104b403706f" "miniconda" verify_py311
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py312_26.5.3-1-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py312_26.5.3-1-Linux-aarch64.sh#9e48caecbad9cc43d8a483584f0a5e1da2754189416a8811aaada3f58aceeb67" "miniconda" verify_py312
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py312_26.5.3-1-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py312_26.5.3-1-Linux-x86_64.sh#ecb43ee4ae30a7a5af87737e9548ceb21f0a10ec55b8dc40d247aa925b80bfec" "miniconda" verify_py312
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py312_26.5.3-1-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py312_26.5.3-1-MacOSX-arm64.sh#f5767f038d5aa8299254b96b7e0db4f086c29e4b3a620fc38fea51974f66abdd" "miniconda" verify_py312
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py312_26.5.3-2-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py312_26.5.3-2-Linux-aarch64.sh#822923b30d789477964ef5589f86de5a72ac4c584bad3b9f72d97190e13d776f" "miniconda" verify_py312
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py312_26.5.3-2-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py312_26.5.3-2-Linux-x86_64.sh#37606f9f03ced8ef60f4ffc76b21dda01728eac8a632dcab316c891cea4fe2f5" "miniconda" verify_py312
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py312_26.5.3-2-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py312_26.5.3-2-MacOSX-arm64.sh#ba7e4dede827e98c468c2dfa3b9a859728844ee8bde72a28eb216869af2d6ce1" "miniconda" verify_py312
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py313_26.5.3-1-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py313_26.5.3-1-Linux-aarch64.sh#4eaf1f2d83ede3ad010afa6ad19bef69893ca4667ba5996f51efb3080c08a70d" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py313_26.5.3-1-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py313_26.5.3-1-Linux-x86_64.sh#7358a5961dc6a4941d087281cd70313728fcc68695735e18a337321bc31c7f51" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py313_26.5.3-1-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py313_26.5.3-1-MacOSX-arm64.sh#c73b91d59c872f472d7a21dadaf0cc70dcff1fadf5bd98200bd15341be2bcbd0" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py313_26.5.3-2-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py313_26.5.3-2-Linux-aarch64.sh#549176858e194a15df5c8326b9f4e1b7ddf1464df47aec4115d0e6d14204e8bb" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py313_26.5.3-2-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py313_26.5.3-2-Linux-x86_64.sh#66f7c434bbdc7a4c5687b7e56cde724f73954d1322ffb273c6e387f12fbcdc03" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py313_26.5.3-2-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py313_26.5.3-2-MacOSX-arm64.sh#0d4d44f5d0740dddc68d381bbc8eb8c056d4792c832a9f0e3b5c663f7dda9d67" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py314_26.5.3-1-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py314_26.5.3-1-Linux-aarch64.sh#781abf4cec9b842f5ed508e7c364ed9951380ba11187b80fe5d9c3dbf9ffccea" "miniconda" verify_py314
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py314_26.5.3-1-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py314_26.5.3-1-Linux-x86_64.sh#42cfece170da342364a78d629e06b94dfd81b0f2717d7655729100d888d606b4" "miniconda" verify_py314
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py314_26.5.3-1-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py314_26.5.3-1-MacOSX-arm64.sh#0cb1e1d43810d3118f7b6cd0095aff48dbde8312a19cb8c44e9a79c38bb48be3" "miniconda" verify_py314
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
export CONDA_PLUGINS_AUTO_ACCEPT_TOS=true
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniconda3-py314_26.5.3-2-Linux-aarch64" "https://repo.anaconda.com/miniconda/Miniconda3-py314_26.5.3-2-Linux-aarch64.sh#999e8761f4dc74fb8d0dc9f74d5374f6708ff81eebbb0af677a4d5307d38b2e5" "miniconda" verify_py314
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniconda3-py314_26.5.3-2-Linux-x86_64" "https://repo.anaconda.com/miniconda/Miniconda3-py314_26.5.3-2-Linux-x86_64.sh#80bc27f13c4de90f10e387aa45e864de4f0860692c1221aef5900009a2b55302" "miniconda" verify_py314
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniconda3-py314_26.5.3-2-MacOSX-arm64" "https://repo.anaconda.com/miniconda/Miniconda3-py314_26.5.3-2-MacOSX-arm64.sh#e830bee5b9c6ebfb67e9df924b259a0d43c451bd17b965f7aa84523b69f7c3eb" "miniconda" verify_py314
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniconda is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
25
plugins/python-build/share/python-build/miniforge3-26.3.2-2
Normal file
25
plugins/python-build/share/python-build/miniforge3-26.3.2-2
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniforge3-26.3.2-2-Linux-aarch64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-2/Miniforge3-26.3.2-2-Linux-aarch64.sh#f4096a92482b30f04534cddb63d8bc929118318deffac71d90fb89dc52359d22" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"Linux-ppc64le" )
|
||||||
|
install_script "Miniforge3-26.3.2-2-Linux-ppc64le.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-2/Miniforge3-26.3.2-2-Linux-ppc64le.sh#8b3527cd4c70e6eb4a6c2c4c41de34c2480f0a5200de2ecb7fe385d186af0869" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniforge3-26.3.2-2-Linux-x86_64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-2/Miniforge3-26.3.2-2-Linux-x86_64.sh#42260ffe3830fb953d5eee1bbb32229ff06aa7c3833c1ed7a9a0420a95685d94" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniforge3-26.3.2-2-MacOSX-arm64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-2/Miniforge3-26.3.2-2-MacOSX-arm64.sh#2657d94152343cff7c06159ac9fc09624d7879fa9575c5a0a324c571c4df0ade" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"MacOSX-x86_64" )
|
||||||
|
install_script "Miniforge3-26.3.2-2-MacOSX-x86_64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-2/Miniforge3-26.3.2-2-MacOSX-x86_64.sh#a755192103de19bb2782685ac78820c2e00702e5f33e6e4f0a3bf3c214f45d69" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniforge is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
25
plugins/python-build/share/python-build/miniforge3-26.3.2-3
Normal file
25
plugins/python-build/share/python-build/miniforge3-26.3.2-3
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
case "$(anaconda_architecture 2>/dev/null || true)" in
|
||||||
|
"Linux-aarch64" )
|
||||||
|
install_script "Miniforge3-26.3.2-3-Linux-aarch64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-3/Miniforge3-26.3.2-3-Linux-aarch64.sh#2c113a69297e612b01ca0f320c22a3107a11f2ab9b573d79ac868a175945ce29" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"Linux-ppc64le" )
|
||||||
|
install_script "Miniforge3-26.3.2-3-Linux-ppc64le.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-3/Miniforge3-26.3.2-3-Linux-ppc64le.sh#df7e80ee070ccc6031e2710eb4cf81ee0012264b587306b5aa3890d3d89edd97" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"Linux-x86_64" )
|
||||||
|
install_script "Miniforge3-26.3.2-3-Linux-x86_64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-3/Miniforge3-26.3.2-3-Linux-x86_64.sh#848194851a98903134187fbb4ab50efe87b003e0c0f808f97644b7524a62bf2c" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"MacOSX-arm64" )
|
||||||
|
install_script "Miniforge3-26.3.2-3-MacOSX-arm64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-3/Miniforge3-26.3.2-3-MacOSX-arm64.sh#59168f1e24d0a4ad9932021170809fca836cd240e183eeeb331d5bcfc0098168" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
"MacOSX-x86_64" )
|
||||||
|
install_script "Miniforge3-26.3.2-3-MacOSX-x86_64.sh" "https://github.com/conda-forge/miniforge/releases/download/26.3.2-3/Miniforge3-26.3.2-3-MacOSX-x86_64.sh#39273e4c89a0a1af4538010615d44ae8f44e1af41007e02def593d20f316b003" "miniconda" verify_py313
|
||||||
|
;;
|
||||||
|
* )
|
||||||
|
{ echo
|
||||||
|
colorize 1 "ERROR"
|
||||||
|
echo ": The binary distribution of Miniforge is not available for $(anaconda_architecture 2>/dev/null || true)."
|
||||||
|
echo
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
From 3fec519d6b326918f39ceb142ef762042ce8365f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ivan Pozdeev <vano@mail.mipt.ru>
|
||||||
|
Date: Fri, 26 Dec 2025 13:39:21 +0300
|
||||||
|
Subject: [PATCH] Recognize an argument to -j in MAKEFLAGS
|
||||||
|
|
||||||
|
---
|
||||||
|
setup.py | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/setup.py b/setup.py
|
||||||
|
index ad8fb81b218..5666deaf67b 100644
|
||||||
|
--- a/setup.py
|
||||||
|
+++ b/setup.py
|
||||||
|
@@ -307,8 +307,8 @@ def __init__(self, dist):
|
||||||
|
self.failed_on_import = []
|
||||||
|
self.missing = []
|
||||||
|
self.disabled_configure = []
|
||||||
|
- if '-j' in os.environ.get('MAKEFLAGS', ''):
|
||||||
|
- self.parallel = True
|
||||||
|
+ if (m:=re.search(r'(?:^|\s)(?:-j|--jobs)(?:\s+|=)?(\d*)',os.environ.get('MAKEFLAGS', ''))) is not None:
|
||||||
|
+ self.parallel = int(m.group(1)) if m.group(1) else True
|
||||||
|
|
||||||
|
def add(self, ext):
|
||||||
|
self.extensions.append(ext)
|
||||||
|
--
|
||||||
|
2.36.1.windows.1
|
||||||
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
From 3fec519d6b326918f39ceb142ef762042ce8365f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ivan Pozdeev <vano@mail.mipt.ru>
|
||||||
|
Date: Fri, 26 Dec 2025 13:39:21 +0300
|
||||||
|
Subject: [PATCH] Recognize an argument to -j in MAKEFLAGS
|
||||||
|
|
||||||
|
---
|
||||||
|
setup.py | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/setup.py b/setup.py
|
||||||
|
index ad8fb81b218..5666deaf67b 100644
|
||||||
|
--- a/setup.py
|
||||||
|
+++ b/setup.py
|
||||||
|
@@ -307,8 +307,8 @@ def __init__(self, dist):
|
||||||
|
self.failed_on_import = []
|
||||||
|
self.missing = []
|
||||||
|
self.disabled_configure = []
|
||||||
|
- if '-j' in os.environ.get('MAKEFLAGS', ''):
|
||||||
|
- self.parallel = True
|
||||||
|
+ if (m:=re.search(r'(?:^|\s)(?:-j|--jobs)(?:\s+|=)?(\d*)',os.environ.get('MAKEFLAGS', ''))) is not None:
|
||||||
|
+ self.parallel = int(m.group(1)) if m.group(1) else True
|
||||||
|
|
||||||
|
def add(self, ext):
|
||||||
|
self.extensions.append(ext)
|
||||||
|
--
|
||||||
|
2.36.1.windows.1
|
||||||
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
From 3fec519d6b326918f39ceb142ef762042ce8365f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ivan Pozdeev <vano@mail.mipt.ru>
|
||||||
|
Date: Fri, 26 Dec 2025 13:39:21 +0300
|
||||||
|
Subject: [PATCH] Recognize an argument to -j in MAKEFLAGS
|
||||||
|
|
||||||
|
---
|
||||||
|
setup.py | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/setup.py b/setup.py
|
||||||
|
index ad8fb81b218..5666deaf67b 100644
|
||||||
|
--- a/setup.py
|
||||||
|
+++ b/setup.py
|
||||||
|
@@ -307,8 +307,8 @@ def __init__(self, dist):
|
||||||
|
self.failed_on_import = []
|
||||||
|
self.missing = []
|
||||||
|
self.disabled_configure = []
|
||||||
|
- if '-j' in os.environ.get('MAKEFLAGS', ''):
|
||||||
|
- self.parallel = True
|
||||||
|
+ if (m:=re.search(r'(?:^|\s)(?:-j|--jobs)(?:\s+|=)?(\d*)',os.environ.get('MAKEFLAGS', ''))) is not None:
|
||||||
|
+ self.parallel = int(m.group(1)) if m.group(1) else True
|
||||||
|
|
||||||
|
def add(self, ext):
|
||||||
|
self.extensions.append(ext)
|
||||||
|
--
|
||||||
|
2.36.1.windows.1
|
||||||
|
|
||||||
|
|
@ -0,0 +1,64 @@
|
||||||
|
From cc13eabc7ce08accf49656e258ba500f74a1dae8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Ronald Oussoren <ronaldoussoren@mac.com>
|
||||||
|
Date: Thu, 28 Dec 2023 09:42:05 +0100
|
||||||
|
Subject: [PATCH] gh-110459: Make sure --with-openssl-rpath works on macOS
|
||||||
|
(#113441)
|
||||||
|
|
||||||
|
* gh-110459: Make sure --with-openssl-rpath works on macOS
|
||||||
|
|
||||||
|
On macOS the `-rpath` linker flag is spelled differently
|
||||||
|
than on on platforms.
|
||||||
|
---
|
||||||
|
.../macOS/2023-12-23-22-41-07.gh-issue-110459.NaMBJy.rst | 2 ++
|
||||||
|
configure | 7 ++++++-
|
||||||
|
configure.ac | 7 ++++++-
|
||||||
|
3 files changed, 14 insertions(+), 2 deletions(-)
|
||||||
|
create mode 100644 Misc/NEWS.d/next/macOS/2023-12-23-22-41-07.gh-issue-110459.NaMBJy.rst
|
||||||
|
|
||||||
|
diff --git a/Misc/NEWS.d/next/macOS/2023-12-23-22-41-07.gh-issue-110459.NaMBJy.rst b/Misc/NEWS.d/next/macOS/2023-12-23-22-41-07.gh-issue-110459.NaMBJy.rst
|
||||||
|
new file mode 100644
|
||||||
|
index 00000000000..44ffd857785
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/Misc/NEWS.d/next/macOS/2023-12-23-22-41-07.gh-issue-110459.NaMBJy.rst
|
||||||
|
@@ -0,0 +1,2 @@
|
||||||
|
+Running ``configure ... --with-openssl-rpath=X/Y/Z`` no longer fails to detect
|
||||||
|
+OpenSSL on macOS.
|
||||||
|
diff --git a/configure b/configure
|
||||||
|
index 7e50abc29d0..6d65d3abc18 100755
|
||||||
|
--- a/configure
|
||||||
|
+++ b/configure
|
||||||
|
@@ -27478,7 +27478,12 @@ then :
|
||||||
|
|
||||||
|
else $as_nop
|
||||||
|
|
||||||
|
- rpath_arg="-Wl,-rpath="
|
||||||
|
+ if test "$ac_sys_system" = "Darwin"
|
||||||
|
+ then
|
||||||
|
+ rpath_arg="-Wl,-rpath,"
|
||||||
|
+ else
|
||||||
|
+ rpath_arg="-Wl,-rpath="
|
||||||
|
+ fi
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
diff --git a/configure.ac b/configure.ac
|
||||||
|
index e064848af9e..bfdabc4474e 100644
|
||||||
|
--- a/configure.ac
|
||||||
|
+++ b/configure.ac
|
||||||
|
@@ -6808,7 +6808,12 @@ AX_CHECK_OPENSSL([have_openssl=yes],[have_openssl=no])
|
||||||
|
AS_VAR_IF([GNULD], [yes], [
|
||||||
|
rpath_arg="-Wl,--enable-new-dtags,-rpath="
|
||||||
|
], [
|
||||||
|
- rpath_arg="-Wl,-rpath="
|
||||||
|
+ if test "$ac_sys_system" = "Darwin"
|
||||||
|
+ then
|
||||||
|
+ rpath_arg="-Wl,-rpath,"
|
||||||
|
+ else
|
||||||
|
+ rpath_arg="-Wl,-rpath="
|
||||||
|
+ fi
|
||||||
|
])
|
||||||
|
|
||||||
|
AC_MSG_CHECKING([for --with-openssl-rpath])
|
||||||
|
--
|
||||||
|
2.46.2.windows.1
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue