Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support building free-threaded CPython #1

Merged
merged 5 commits into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 43 additions & 25 deletions .github/workflows/build-python-packages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ on:
inputs:
VERSION:
description: 'Python version to build and upload'
default: '3.12.3'
default: '3.13.0'
required: true
PUBLISH_RELEASES:
description: 'Whether to publish releases'
required: true
type: boolean
default: false
THREADING_BUILD_MODES:
description: 'CPython threading build modes'
required: true
type: str
default: 'freethreaded'
PLATFORMS:
description: 'Platforms for execution in "os" or "os_arch" format (arch is "x64" by default)'
required: true
default: 'ubuntu-20.04,ubuntu-22.04,ubuntu-22.04_arm64,ubuntu-24.04,ubuntu-24.04_arm64,macos-13_x64,macos-14_arm64,windows-2019_x64,windows-2019_x86,windows-2019_arm64'
default: 'ubuntu-20.04,ubuntu-22.04,ubuntu-24.04,macos-13_x64,macos-14_arm64,windows-2019_x64,windows-2019_x86'
pull_request:
paths-ignore:
- 'versions-manifest.json'
Expand All @@ -40,32 +45,42 @@ jobs:
id: generate-matrix
run: |
[String[]]$configurations = "${{ inputs.platforms || 'ubuntu-20.04,ubuntu-22.04,ubuntu-22.04_arm64,ubuntu-24.04,ubuntu-24.04_arm64,macos-13,macos-14_arm64,windows-2019_x64,windows-2019_x86,windows-2019_arm64' }}".Split(",").Trim()
[String[]]$buildModes = "${{ inputs.threading_build_modes || 'default' }}".Split(",").Trim()
$matrix = @()
foreach ($configuration in $configurations) {
$parts = $configuration.Split("_")
$os = $parts[0]
$arch = if ($parts[1]) {$parts[1]} else {"x64"}
switch -wildcard ($os) {
"*ubuntu*" { $platform = $os.Replace("ubuntu","linux")}
"*macos*" { $platform = 'darwin' }
"*windows*" { $platform = 'win32' }
}
if ($configuration -eq "ubuntu-22.04_arm64") {
$os = "setup-actions-ubuntu-arm64-2-core"
}
elseif ($configuration -eq "ubuntu-24.04_arm64") {
$os = "setup-actions-ubuntu24-arm64-2-core"
}
elseif ($configuration -eq "windows-2019_arm64") {
$os = "setup-actions-windows-arm64-4-core"
}
$matrix += @{
'platform' = $platform
'os' = $os
'arch' = $arch
foreach ($buildMode in $buildModes) {
$parts = $configuration.Split("_")
$os = $parts[0]
$arch = if ($parts[1]) {$parts[1]} else {"x64"}
switch -wildcard ($os) {
"*ubuntu*" { $platform = $os.Replace("ubuntu","linux")}
"*macos*" { $platform = 'darwin' }
"*windows*" { $platform = 'win32' }
}
if ($configuration -eq "ubuntu-22.04_arm64") {
$os = "setup-actions-ubuntu-arm64-2-core"
}
elseif ($configuration -eq "ubuntu-24.04_arm64") {
$os = "setup-actions-ubuntu24-arm64-2-core"
}
elseif ($configuration -eq "windows-2019_arm64") {
$os = "setup-actions-windows-arm64-4-core"
}
if ($buildMode -eq "freethreaded") {
if ([semver]"${{ inputs.VERSION }}" -lt [semver]"3.13.0") {
continue;
}
$arch += "-freethreaded"
}
$matrix += @{
'platform' = $platform
'os' = $os
'arch' = $arch
}
}
}
echo "matrix=$($matrix | ConvertTo-Json -Compress -AsArray)" >> $env:GITHUB_OUTPUT
Expand Down Expand Up @@ -201,6 +216,9 @@ jobs:
python-version: ${{ env.VERSION }}
architecture: ${{ matrix.arch }}

- name: Python version
run: python -VVV

- name: Verbose sysconfig dump
if: runner.os == 'Linux' || runner.os == 'macOS'
run: python ./sources/python-config-output.py
Expand Down
43 changes: 43 additions & 0 deletions .github/workflows/create-pr-to-update-manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This reusable workflow is used by actions/*-versions repositories
# It is designed to create a PR with update of versions-manifest.json when a new release is published
# The GITHUB_TOKEN secret is used to create versions-manifest.json and publish related PR

name: Create Pull Request (called indirectly)
on:
workflow_call:
inputs:
tool-name:
description: 'Name of the tool for which PR is created'
required: true
type: string

defaults:
run:
shell: pwsh

jobs:
create_pr:
name: Create Pull Request
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true

- name: Create versions-manifest.json
run: |
./nogil-helpers/manifest-generator.ps1 -RepositoryFullName "$env:GITHUB_REPOSITORY" `
-GitHubAccessToken "${{ secrets.GITHUB_TOKEN }}" `
-OutputFile "./versions-manifest.json" `
-ConfigurationFile "./config/${{ inputs.tool-name }}-manifest-config.json"
- name: Create GitHub PR
run: |
$formattedDate = Get-Date -Format "MM/dd/yyyy"
./helpers/github/create-pull-request.ps1 `
-RepositoryFullName "$env:GITHUB_REPOSITORY" `
-AccessToken "${{ secrets.GITHUB_TOKEN }}" `
-BranchName "update-versions-manifest-file" `
-CommitMessage "Update versions-manifest" `
-PullRequestTitle "[versions-manifest] Update for release from ${formattedDate}" `
-PullRequestBody "Update versions-manifest.json for release from ${formattedDate}"
2 changes: 1 addition & 1 deletion .github/workflows/create-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:

jobs:
create-pr:
uses: actions/versions-package-tools/.github/workflows/create-pr-to-update-manifest.yml@main
uses: ./.github/workflows/create-pr-to-update-manifest.yml
with:
tool-name: "python"
secrets: inherit
33 changes: 33 additions & 0 deletions builders/macos-python-builder.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,37 @@ class macOSPythonBuilder : NixPythonBuilder {
return $pkgLocation
}

[string] GetFrameworkName() {
<#
.SYNOPSIS
Get the Python installation Package name.
#>

if ($this.IsFreeThreaded()) {
return "PythonT.framework"
} else {
return "Python.framework"
}
}

[string] GetPkgChoices() {
<#
.SYNOPSIS
Reads the configuration XML file for the Python installer
#>

$config = if ($this.IsFreeThreaded()) { "freethreaded" } else { "default" }
$choicesFile = Join-Path $PSScriptRoot "../config/macos-pkg-choices-$($config).xml"
$choicesTemplate = Get-Content -Path $choicesFile -Raw

$variablesToReplace = @{
"{{__VERSION_MAJOR_MINOR__}}" = "$($this.Version.Major).$($this.Version.Minor)";
}

$variablesToReplace.keys | ForEach-Object { $choicesTemplate = $choicesTemplate.Replace($_, $variablesToReplace[$_]) }
return $choicesTemplate
}

[void] CreateInstallationScriptPkg() {
<#
.SYNOPSIS
Expand All @@ -165,6 +196,8 @@ class macOSPythonBuilder : NixPythonBuilder {
"{{__VERSION_FULL__}}" = $this.Version;
"{{__PKG_NAME__}}" = $this.GetPkgName();
"{{__ARCH__}}" = $this.Architecture;
"{{__FRAMEWORK_NAME__}}" = $this.GetFrameworkName();
"{{__PKG_CHOICES__}}" = $this.GetPkgChoices();
}

$variablesToReplace.keys | ForEach-Object { $installationTemplateContent = $installationTemplateContent.Replace($_, $variablesToReplace[$_]) }
Expand Down
2 changes: 1 addition & 1 deletion builders/nix-python-builder.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class NixPythonBuilder : PythonBuilder {
Write-Debug "make Python $($this.Version)-$($this.Architecture) $($this.Platform)"
$buildOutputLocation = New-Item -Path $this.WorkFolderLocation -Name "build_output.txt" -ItemType File

Execute-Command -Command "make 2>&1 | tee $buildOutputLocation" -ErrorAction Continue
Execute-Command -Command "make 2>&1 | tee $buildOutputLocation" -ErrorAction Continue
Execute-Command -Command "make install" -ErrorAction Continue

Write-Debug "Done; Make log location: $buildOutputLocation"
Expand Down
18 changes: 18 additions & 0 deletions builders/python-builder.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ class PythonBuilder {
return "$($this.Version.Major).$($this.Version.Minor).$($this.Version.Patch)"
}

[string] GetHardwareArchitecture() {
<#
.SYNOPSIS
The hardware architecture (x64, arm64) without any Python free threading suffix.
#>

return $this.Architecture.Replace("-freethreaded", "")
}

[bool] IsFreeThreaded() {
<#
.SYNOPSIS
Check if Python version is free threaded.
#>

return $this.Architecture.EndsWith("-freethreaded")
}

[void] PreparePythonToolcacheLocation() {
<#
.SYNOPSIS
Expand Down
8 changes: 8 additions & 0 deletions builders/ubuntu-python-builder.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ class UbuntuPythonBuilder : NixPythonBuilder {
$configureString += " --enable-shared"
$configureString += " --enable-optimizations"

if ($this.IsFreeThreaded()) {
if ($this.Version -lt "3.13.0") {
Write-Host "Python versions lower than 3.13.0 do not support free threading"
exit 1
}
$configureString += " --disable-gil"
}

### Compile with support of loadable sqlite extensions.
### Link to documentation (https://docs.python.org/3/library/sqlite3.html#sqlite3.Connection.enable_load_extension)
$configureString += " --enable-loadable-sqlite-extensions"
Expand Down
5 changes: 3 additions & 2 deletions builders/win-python-builder.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,13 @@ class WinPythonBuilder : PythonBuilder {
#>

$ArchitectureExtension = ""
if ($this.Architecture -eq "x64") {
if ($this.GetHardwareArchitecture() -eq "x64") {
if ($this.Version -ge "3.5") {
$ArchitectureExtension = "-amd64"
} else {
$ArchitectureExtension = ".amd64"
}
}elseif ($this.Architecture -eq "arm64") {
} elseif ($this.GetHardwareArchitecture() -eq "arm64") {
$ArchitectureExtension = "-arm64"
}

Expand Down Expand Up @@ -113,6 +113,7 @@ class WinPythonBuilder : PythonBuilder {

$variablesToReplace = @{
"{{__ARCHITECTURE__}}" = $this.Architecture;
"{{__HARDWARE_ARCHITECTURE__}}" = $this.GetHardwareArchitecture();
"{{__VERSION__}}" = $this.Version;
"{{__PYTHON_EXEC_NAME__}}" = $pythonExecName
}
Expand Down
8 changes: 8 additions & 0 deletions config/macos-pkg-choices-default.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
</dict>
</array>
</plist>
14 changes: 14 additions & 0 deletions config/macos-pkg-choices-freethreaded.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>attributeSetting</key>
<integer>1</integer>
<key>choiceAttribute</key>
<string>selected</string>
<key>choiceIdentifier</key>
<string>org.python.Python.PythonTFramework-{{__VERSION_MAJOR_MINOR__}}</string>
</dict>
</array>
</plist>
2 changes: 1 addition & 1 deletion config/python-manifest-config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"regex": "python-\\d+\\.\\d+\\.\\d+-(\\w+\\.\\d+)?-?(\\w+)-(\\d+\\.\\d+)?-?((x|arm)\\d+)",
"regex": "python-\\d+\\.\\d+\\.\\d+-(\\w+\\.\\d+)?-?(\\w+)-(\\d+\\.\\d+)?-?((x|arm)\\d+(-freethreaded)?)",
"groups": {
"arch": 4,
"platform": 2,
Expand Down
20 changes: 17 additions & 3 deletions installers/macos-pkg-setup-template.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ set -e

PYTHON_FULL_VERSION="{{__VERSION_FULL__}}"
PYTHON_PKG_NAME="{{__PKG_NAME__}}"
PYTHON_FRAMEWORK_NAME="{{__FRAMEWORK_NAME__}}"
PYTHON_PKG_CHOICES=$(cat << 'EOF'
{{__PKG_CHOICES__}}
EOF
)
ARCH="{{__ARCH__}}"
MAJOR_VERSION=$(echo $PYTHON_FULL_VERSION | cut -d '.' -f 1)
MINOR_VERSION=$(echo $PYTHON_FULL_VERSION | cut -d '.' -f 2)
Expand All @@ -20,7 +25,7 @@ fi
PYTHON_TOOLCACHE_PATH=$TOOLCACHE_ROOT/Python
PYTHON_TOOLCACHE_VERSION_PATH=$PYTHON_TOOLCACHE_PATH/$PYTHON_FULL_VERSION
PYTHON_TOOLCACHE_VERSION_ARCH_PATH=$PYTHON_TOOLCACHE_VERSION_PATH/$ARCH
PYTHON_FRAMEWORK_PATH="/Library/Frameworks/Python.framework/Versions/${MAJOR_VERSION}.${MINOR_VERSION}"
PYTHON_FRAMEWORK_PATH="/Library/Frameworks/${PYTHON_FRAMEWORK_NAME}/Versions/${MAJOR_VERSION}.${MINOR_VERSION}"
PYTHON_APPLICATION_PATH="/Applications/Python ${MAJOR_VERSION}.${MINOR_VERSION}"

echo "Check if Python hostedtoolcache folder exist..."
Expand All @@ -38,8 +43,11 @@ else
done
fi

PYTHON_PKG_CHOICES_FILES=$(mktemp)
echo "$PYTHON_PKG_CHOICES" > $PYTHON_PKG_CHOICES_FILES

echo "Install Python binaries from prebuilt package"
sudo installer -pkg $PYTHON_PKG_NAME -target /
sudo installer -pkg $PYTHON_PKG_NAME -applyChoiceChangesXML $PYTHON_PKG_CHOICES_FILES -target /

echo "Create hostedtoolcach symlinks (Required for the backward compatibility)"
echo "Create Python $PYTHON_FULL_VERSION folder"
Expand All @@ -53,7 +61,9 @@ ln -s "${PYTHON_FRAMEWORK_PATH}/lib" lib

echo "Create additional symlinks (Required for the UsePythonVersion Azure Pipelines task and the setup-python GitHub Action)"
ln -s ./bin/$PYTHON_MAJOR_DOT_MINOR python
chmod +x python

# Note that bin is a symlink so referencing .. from bin will not work as expected
cd bin/

# This symlink already exists if Python version with the same major.minor version is installed,
Expand All @@ -62,11 +72,15 @@ if [ ! -f $PYTHON_MAJOR_MINOR ]; then
ln -s $PYTHON_MAJOR_DOT_MINOR $PYTHON_MAJOR_MINOR
fi

if [ ! -f $PYTHON_MAJOR ]; then
ln -s $PYTHON_MAJOR_DOT_MINOR $PYTHON_MAJOR
fi

if [ ! -f python ]; then
ln -s $PYTHON_MAJOR_DOT_MINOR python
fi

chmod +x ../python $PYTHON_MAJOR $PYTHON_MAJOR_DOT_MINOR $PYTHON_MAJOR_MINOR python
chmod +x $PYTHON_MAJOR $PYTHON_MAJOR_DOT_MINOR $PYTHON_MAJOR_MINOR python

echo "Upgrading pip..."
export PIP_ROOT_USER_ACTION=ignore
Expand Down
Loading
Loading