diff --git a/.github/workflows/build-python-packages.yml b/.github/workflows/build-python-packages.yml index 862a0828..05c85b8e 100644 --- a/.github/workflows/build-python-packages.yml +++ b/.github/workflows/build-python-packages.yml @@ -20,7 +20,7 @@ on: PLATFORMS: description: 'Platforms for execution in "os" or "os_arch" format (arch is "x64" by default)' required: true - default: 'ubuntu-22.04,ubuntu-22.04_arm64,ubuntu-24.04,ubuntu-24.04_arm64,macos-15-intel_x64,macos-14_arm64,windows-2022_x64,windows-2022_x86,windows-11_arm64' + default: 'ubuntu-22.04,ubuntu-22.04_arm64,ubuntu-24.04,ubuntu-24.04_arm64,rhel-9,rhel-10,macos-15-intel_x64,macos-14_arm64,windows-2022_x64,windows-2022_x86,windows-11_arm64' pull_request: paths-ignore: - 'versions-manifest.json' @@ -44,7 +44,7 @@ jobs: - name: Generate execution matrix id: generate-matrix run: | - [String[]]$configurations = "${{ inputs.platforms || 'ubuntu-22.04,ubuntu-22.04_arm64,ubuntu-24.04,ubuntu-24.04_arm64,macos-15-intel,macos-14_arm64,windows-2022_x64,windows-2022_x86,windows-11_arm64' }}".Split(",").Trim() + [String[]]$configurations = "${{ inputs.platforms || 'ubuntu-22.04,ubuntu-22.04_arm64,ubuntu-24.04,ubuntu-24.04_arm64,rhel-9,rhel-10,macos-15-intel,macos-14_arm64,windows-2022_x64,windows-2022_x86,windows-11_arm64' }}".Split(",").Trim() [String[]]$buildModes = "${{ inputs.threading_build_modes || 'default' }}".Split(",").Trim() $matrix = @() @@ -55,6 +55,7 @@ jobs: $arch = if ($parts[1]) {$parts[1]} else {"x64"} switch -wildcard ($os) { "*ubuntu*" { $platform = $os.Replace("ubuntu","linux"); if ($arch -eq "arm64" ) { $os = "${os}-arm" } } + "*rhel*" { $platform = $os.Replace("rhel","rhel"); $os = $os.Replace("rhel","setup-actions-rhel") } "*macos*" { $platform = 'darwin' } "*windows*" { $platform = 'win32'; if ($arch -eq "arm64" ) { $os = "${os}-arm" } } } @@ -85,6 +86,14 @@ jobs: env: ARTIFACT_NAME: python-${{ inputs.VERSION || '3.13.7' }}-${{ matrix.platform }}-${{ matrix.arch }} steps: + - name: Install prerequisites + if: startsWith(matrix.platform, 'rhel') + shell: bash + run: | + RHEL_VERSION=$(rpm -E %rhel) + sudo dnf install -y "https://packages.microsoft.com/config/rhel/${RHEL_VERSION}/packages-microsoft-prod.rpm" + sudo dnf install -y powershell git + - name: Check out repository code uses: actions/checkout@v6 with: @@ -112,6 +121,14 @@ jobs: env: ARTIFACT_NAME: python-${{ inputs.VERSION || '3.13.7' }}-${{ matrix.platform }}-${{ matrix.arch }} steps: + - name: Install prerequisites + if: startsWith(matrix.platform, 'rhel') + shell: bash + run: | + RHEL_VERSION=$(rpm -E %rhel) + sudo dnf install -y "https://packages.microsoft.com/config/rhel/${RHEL_VERSION}/packages-microsoft-prod.rpm" + sudo dnf install -y powershell git tk sqlite-libs gcc + - name: Check out repository code uses: actions/checkout@v6 with: diff --git a/builders/build-python.ps1 b/builders/build-python.ps1 index cced69e6..83b9e6aa 100644 --- a/builders/build-python.ps1 +++ b/builders/build-python.ps1 @@ -1,5 +1,6 @@ using module "./win-python-builder.psm1" using module "./ubuntu-python-builder.psm1" +using module "./rhel-python-builder.psm1" using module "./macos-python-builder.psm1" <# @@ -58,6 +59,8 @@ function Get-PythonBuilder { if ($Platform -match 'win32') { $builder = [WinPythonBuilder]::New($Version, $Architecture, $Platform) + } elseif ($Platform -match 'rhel') { + $builder = [RhelPythonBuilder]::New($Version, $Architecture, $Platform) } elseif ($Platform -match 'linux') { $builder = [UbuntuPythonBuilder]::New($Version, $Architecture, $Platform) } elseif ($Platform -match 'darwin') { diff --git a/builders/rhel-python-builder.psm1 b/builders/rhel-python-builder.psm1 new file mode 100644 index 00000000..19090cd7 --- /dev/null +++ b/builders/rhel-python-builder.psm1 @@ -0,0 +1,88 @@ +using module "./nix-python-builder.psm1" + +class RhelPythonBuilder : NixPythonBuilder { + <# + .SYNOPSIS + RHEL Python builder class. + + .DESCRIPTION + Contains methods that required to build RHEL Python artifact from sources. Inherited from base NixPythonBuilder. + + .PARAMETER platform + The full name of platform for which Python should be built. + + .PARAMETER version + The version of Python that should be built. + + #> + + RhelPythonBuilder( + [semver] $version, + [string] $architecture, + [string] $platform + ) : Base($version, $architecture, $platform) { } + + [void] Configure() { + <# + .SYNOPSIS + Execute configure script with required parameters. + #> + + $pythonBinariesLocation = $this.GetFullPythonToolcacheLocation() + + ### To build Python with SO we must pass full path to lib folder to the linker + $env:LDFLAGS="-Wl,--rpath=${pythonBinariesLocation}/lib" + $configureString = "./configure" + $configureString += " --prefix=$pythonBinariesLocation" + $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" + + Write-Host "The passed configure options are: " + Write-Host $configureString + + Execute-Command -Command $configureString + } + + [void] PrepareEnvironment() { + <# + .SYNOPSIS + Prepare system environment by installing dependencies and required packages. + #> + + if ($this.Version -lt "3.9.0") { + Write-Host "Python versions lower than 3.9.0 are not supported" + exit 1 + } + + ### Install dependent packages + @( + "make", + "gcc", + "gcc-c++", + "openssl-devel", + "zlib-devel", + "bzip2-devel", + "sqlite-devel", + "ncurses-devel", + "readline-devel", + "gdbm-devel", + "xz-devel", + "libffi-devel", + "tk-devel" + ) | ForEach-Object { + Execute-Command -Command "sudo dnf install -y $_" + } + } +} diff --git a/config/python-manifest-config.json b/config/python-manifest-config.json index 7390b920..3e98f677 100644 --- a/config/python-manifest-config.json +++ b/config/python-manifest-config.json @@ -1,7 +1,7 @@ { - "regex": "python-\\d+\\.\\d+\\.\\d+-(\\w+\\.\\d+)?-?(\\w+)-(\\d+\\.\\d+)?-?((x|arm)\\d+(-freethreaded)?)", + "regex": "python-\\d+\\.\\d+\\.\\d+-(\\w+\\.\\d+)?-?(\\w+)-(\\d+(\\.\\d+)?)?-?((x|arm)\\d+(-freethreaded)?)", "groups": { - "arch": 4, + "arch": 5, "platform": 2, "platform_version": 3 } diff --git a/tests/ManifestConfig.Tests.ps1 b/tests/ManifestConfig.Tests.ps1 index 5d0ada45..09cb56ef 100644 --- a/tests/ManifestConfig.Tests.ps1 +++ b/tests/ManifestConfig.Tests.ps1 @@ -14,7 +14,11 @@ $stableTestCases = @( @{ ReleaseName = "python-3.13.0-linux-20.04-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "linux"; platform_version = "20.04"; arch = "x64-freethreaded"} }, @{ ReleaseName = "python-3.13.0-linux-22.04-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "linux"; platform_version = "22.04"; arch = "x64-freethreaded"} }, @{ ReleaseName = "python-3.13.0-win32-x64-freethreaded.zip"; ExpectedResult = @{ platform = "win32"; platform_version = $null; arch = "x64-freethreaded"} }, - @{ ReleaseName = "python-3.13.0-win32-x86-freethreaded.zip"; ExpectedResult = @{ platform = "win32"; platform_version = $null; arch = "x86-freethreaded"} } + @{ ReleaseName = "python-3.13.0-win32-x86-freethreaded.zip"; ExpectedResult = @{ platform = "win32"; platform_version = $null; arch = "x86-freethreaded"} }, + @{ ReleaseName = "python-3.13.0-rhel-9-x64.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "9"; arch = "x64"} }, + @{ ReleaseName = "python-3.13.0-rhel-10-x64.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "10"; arch = "x64"} }, + @{ ReleaseName = "python-3.13.0-rhel-9-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "9"; arch = "x64-freethreaded"} }, + @{ ReleaseName = "python-3.13.0-rhel-10-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "10"; arch = "x64-freethreaded"} } ) | ForEach-Object { $_.Configuration = $Configuration; $_ } @@ -29,7 +33,11 @@ $unstableTestCases = @( @{ ReleaseName = "python-3.14.0-alpha.5-linux-20.04-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "linux"; platform_version = "20.04"; arch = "x64-freethreaded"} }, @{ ReleaseName = "python-3.14.0-alpha.5-linux-22.04-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "linux"; platform_version = "22.04"; arch = "x64-freethreaded"} }, @{ ReleaseName = "python-3.14.0-alpha.5-win32-x64-freethreaded.zip"; ExpectedResult = @{ platform = "win32"; platform_version = $null; arch = "x64-freethreaded"} }, - @{ ReleaseName = "python-3.14.0-alpha.5-win32-x86-freethreaded.zip"; ExpectedResult = @{ platform = "win32"; platform_version = $null; arch = "x86-freethreaded"} } + @{ ReleaseName = "python-3.14.0-alpha.5-win32-x86-freethreaded.zip"; ExpectedResult = @{ platform = "win32"; platform_version = $null; arch = "x86-freethreaded"} }, + @{ ReleaseName = "python-3.14.0-alpha.5-rhel-9-x64.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "9"; arch = "x64"} }, + @{ ReleaseName = "python-3.14.0-alpha.5-rhel-10-x64.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "10"; arch = "x64"} }, + @{ ReleaseName = "python-3.14.0-alpha.5-rhel-9-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "9"; arch = "x64-freethreaded"} }, + @{ ReleaseName = "python-3.14.0-alpha.5-rhel-10-x64-freethreaded.tar.gz"; ExpectedResult = @{ platform = "rhel"; platform_version = "10"; arch = "x64-freethreaded"} } ) | ForEach-Object { $_.Configuration = $Configuration; $_ } diff --git a/tests/python-tests.ps1 b/tests/python-tests.ps1 index 61cfe695..e1dc7a74 100644 --- a/tests/python-tests.ps1 +++ b/tests/python-tests.ps1 @@ -71,7 +71,7 @@ Describe "Tests" { "pip uninstall requests -y" | Should -ReturnZeroExitCode } - if (IsNixPlatform $Platform) { + if ((IsNixPlatform $Platform) -or ($Platform -match "rhel")) { It "Check for failed modules in build_output" { $buildOutputLocation = Join-Path $env:RUNNER_TEMP "build_output.txt" diff --git a/tests/sources/python-config-test.py b/tests/sources/python-config-test.py index cc47ec02..4aaa226f 100644 --- a/tests/sources/python-config-test.py +++ b/tests/sources/python-config-test.py @@ -34,7 +34,8 @@ if pkg_installer: expected_lib_dir_path = f'/Library/Frameworks/{framework_name}/Versions/{version_major}.{version_minor}/lib' else: - expected_lib_dir_path = f'{os.getenv("AGENT_TOOLSDIRECTORY")}/Python/{version}/{architecture}/lib' + tools_dir = os.getenv("AGENT_TOOLSDIRECTORY") or os.getenv("RUNNER_TOOL_CACHE") + expected_lib_dir_path = f'{tools_dir}/Python/{version}/{architecture}/lib' # Check modules ### Validate libraries path