Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ec5fb7d
Fix and test Android build
o01eg Jan 30, 2026
79a8d4a
Make it buildable
o01eg Jan 30, 2026
6c61e9b
Use working Python 3.10
o01eg Jan 30, 2026
fbdcfd1
Fix floating point endianness checks
Apr 17, 2020
392b46b
Don't build fcntl and grp extensions for Android
o01eg Jan 30, 2026
b3ecef4
Don't generate sysconfigdata for Android
o01eg Jan 30, 2026
651dba6
Add platform triplets for Android
o01eg Jan 30, 2026
4a8b9d1
Some more Android fixes
o01eg Feb 18, 2026
cc787b0
Reorganize CI for Android
o01eg Feb 18, 2026
0ef0e78
Don't install old CMake
o01eg Feb 18, 2026
beb773d
Install libpython pdb file when building shared libraries
adrianbroher Mar 21, 2016
cd9d486
Don't export python cmake targets
adrianbroher Mar 21, 2016
8a4457e
Change pathes to match current SDK layout
adrianbroher Mar 21, 2016
3970eaa
Disable MSVC specific compiler warnings
adrianbroher Sep 6, 2016
5f0da34
Add Mac OSX framework support
adrianbroher Apr 9, 2016
a4dada2
Install binaries, libraries, headers files and auxilliary files into …
adrianbroher Sep 17, 2016
ce8f978
Change PYTHONPATH and PYTHONHOME for framework builds
adrianbroher Sep 18, 2016
7ffebce
Install platform library files on MacOSX
adrianbroher Sep 20, 2016
41b6cbe
Install configure Makefile and pyconfig.h for sysconfig into INT dir
adrianbroher Sep 29, 2016
6fcc027
Disable generation of python compiled bytecode when calling python in…
adrianbroher Oct 19, 2016
123f792
Populate Makefile variable with MacOSX framework variables
adrianbroher Oct 19, 2016
7c5500d
Set INSTALL_NAME_DIR only for non-frameworks on OSX
adrianbroher Nov 15, 2016
3a8f81f
Set python exe RPATH to search for libpython in Frameworks
adrianbroher Mar 23, 2017
f437e1f
Use python<major>.<minor> as executable output name
adrianbroher Apr 15, 2018
ab4833d
Copy 3.6 patches to 3.7
o01eg May 19, 2022
56e13f8
Copy 3.7 patches to 3.8
o01eg May 25, 2022
0df1e45
Copy 3.8 patches to 3.9
o01eg Apr 10, 2023
d85c182
Copy patches 3.10
o01eg Feb 22, 2025
83cc030
Fix 3.10 patches
o01eg Feb 22, 2025
173d050
Fix 3.10 patches again
o01eg Feb 22, 2025
15bf16a
Copy patches 3.11
o01eg Feb 23, 2025
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
48 changes: 48 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,58 @@ jobs:
CC: ${{ matrix.c-compiler }}
CXX: ${{ matrix.cxx-compiler }}

tests-android:
runs-on: ${{ matrix.host }}
strategy:
matrix:
ndk-arch: [x86_64, arm64-v8a, x86, armeabi-v7a]
host: [ubuntu-24.04]
python-version: [3.8.20, 3.9.22, 3.10.17]
name: python-android-${{ matrix.ndk-arch }}-${{ matrix.python-version }}-${{ matrix.host }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
path: src
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Make build and prefix directories
run: |
mkdir build
mkdir install-prefix
- name: run action
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ startsWith(matrix.ndk-arch, 'x86') && 21 || 30 }}
target: ${{ startsWith(matrix.ndk-arch, 'x86') && 'default' || 'google_apis' }}
arch: ${{ matrix.ndk-arch == 'x86' && 'x86' || 'x86_64' }}
profile: Galaxy Nexus
cores: 2
ram-size: 2048M
sdcard-path-or-size: 100M
emulator-build: 7425822 # https://github.com/ReactiveCircus/android-emulator-runner/issues/160
avd-name: test
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
working-directory: build/
ndk: 23.0.7421159
channel: beta
script: |
adb devices
cmake -DANDROID_NDK=/usr/local/lib/android/sdk/ndk/23.0.7421159/ -DCMAKE_INSTALL_PREFIX:PATH=../install-prefix -DCMAKE_TOOLCHAIN_FILE=/usr/local/lib/android/sdk/ndk/23.0.7421159/build/cmake/android.toolchain.cmake -DANDROID_ABI=${{ matrix.ndk-arch }} -DCMAKE_CROSSCOMPILING_EMULATOR=$(pwd)/../src/.github/workflows/adb-emu.sh -DANDROID_ALLOW_UNDEFINED_SYMBOLS=On -DENABLE_DECIMAL=Off -DENABLE_CTYPES=Off -DENABLE_CODECS_JP=OFF -DENABLE_CODECS_KR=OFF -DENABLE_CODECS_TW=OFF -DENABLE_MULTIBYTECODEC=OFF -DENABLE_CODECS_CN=OFF -DENABLE_CODECS_HK=OFF -DENABLE_CODECS_ISO2022=OFF -DBUILD_EXTENSIONS_AS_BUILTIN=On -DANDROID_PLATFORM=android-21 -DPYTHON_VERSION=${{ matrix.python-version }} $(pwd)/../src/
cmake --build . -- VERBOSE=1
cmake --build . --target install


pass: # This job does nothing and is only used for the branch protection
if: always()
needs:
- tests
- tests-android

runs-on: ubuntu-latest

Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/adb-emu.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/sh
adb push "$1" /data/local/tmp/ 1>/dev/null 2>/dev/null
if [ $# -eq 1 ]; then
adb shell /data/local/tmp/$(basename $1)
elif [ $# -eq 3 ]; then
adb push "$2" /data/local/tmp/ 1>/dev/null 2>/dev/null
adb shell /data/local/tmp/$(basename $1) /data/local/tmp/$(basename $2) /data/local/tmp/$(basename $3)
adb pull /data/local/tmp/$(basename $3) "$3" 1>/dev/null 2>/dev/null
fi
86 changes: 57 additions & 29 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,25 @@ else()
set(BUILD_WININST_ALWAYS OFF)
endif()

if(APPLE)
option(BUILD_FRAMEWORK "Build a Mac OSX Framework" OFF)
if(BUILD_FRAMEWORK)
set(BUILD_LIBPYTHON_SHARED ON CACHE BOOL "Forced to ON because of BUILD_FRAMEWORK=ON" FORCE)
endif()
else()
set(BUILD_FRAMEWORK OFF)
endif()
if(BUILD_FRAMEWORK)
set(FRAMEWORK_NAME Python)
set(FRAMEWORK_PREFIX ${CMAKE_INSTALL_PREFIX})
set(FRAMEWORK_DIR_ROOT ${FRAMEWORK_NAME}.framework)
set(FRAMEWORK_DIR_PREFIX ${FRAMEWORK_DIR_ROOT}/Versions/${PY_VERSION_MAJOR}.${PY_VERSION_MINOR})
else()
set(FRAMEWORK_NAME "")
set(FRAMEWORK_PREFIX "")
set(FRAMEWORK_DIR_ROOT "no-framework")
set(FRAMEWORK_DIR_PREFIX "")
endif()
option(INSTALL_DEVELOPMENT "Install files required to develop C extensions" ON)
option(INSTALL_MANUAL "Install man files" ON)
option(INSTALL_TEST "Install test files" ON)
Expand Down Expand Up @@ -502,18 +521,25 @@ if(USE_LIB64)
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS ON)
set(LIBDIR "lib64")
endif()
set(PYTHONHOME "${LIBDIR}")
if(UNIX)
set(PYTHONHOME "${PYTHONHOME}/${LIBPYTHON}")
if(BUILD_FRAMEWORK)
set(LIBDIR ${FRAMEWORK_DIR_PREFIX}/lib)
endif()
set(PYTHONHOME "${LIBDIR}/${LIBPYTHON}")

# Install tree directory
set(BIN_INSTALL_DIR bin) # Contains the python executable
if(INSTALL_WINDOWS_TRADITIONAL)
set(BIN_INSTALL_DIR .) # Contains the python executable
endif()
if(BUILD_FRAMEWORK)
set(BIN_INSTALL_DIR ${FRAMEWORK_DIR_PREFIX}/bin)
endif()
set(SHARE_INSTALL_DIR share)
if(BUILD_FRAMEWORK)
set(SHARE_INSTALL_DIR ${FRAMEWORK_DIR_PREFIX}/share)
endif()
set(LD_VERSION ${LIBPYTHON_VERSION}${ABIFLAGS})
set(CONFIG_INSTALL_DIR share/${LIBPYTHON})
set(CONFIG_INSTALL_DIR ${SHARE_INSTALL_DIR}/${LIBPYTHON})
set(EXTENSION_INSTALL_DIR ${PYTHONHOME}/lib-dynload)

if (${LIBPYTHON_VERSION} GREATER 3.1)
Expand All @@ -527,15 +553,19 @@ if(INSTALL_WINDOWS_TRADITIONAL)
endif()
set(INCLUDE_INSTALL_DIR include/python${LD_VERSION})
if(MSVC)
set(INCLUDE_INSTALL_DIR include)
set(INCLUDE_INSTALL_DIR include/python${PY_VERSION_MAJOR}.${PY_VERSION_MINOR})
endif()
if(BUILD_FRAMEWORK)
set(REL_INCLUDE_INSTALL_DIR include/python${PY_VERSION_MAJOR}.${PY_VERSION_MINOR})
set(INCLUDE_INSTALL_DIR ${FRAMEWORK_DIR_PREFIX}/include/python${PY_VERSION_MAJOR}.${PY_VERSION_MINOR})
endif()
# Build tree directory
set(BIN_BUILD_DIR ${PROJECT_BINARY_DIR}/bin)
set(CONFIG_BUILD_DIR ${PROJECT_BINARY_DIR}/${CONFIG_INSTALL_DIR})
set(EXTENSION_BUILD_DIR ${PROJECT_BINARY_DIR}/${PYTHONHOME}/lib-dynload)
set(INCLUDE_BUILD_DIR ${SRC_DIR}/Include)

set(ARCHIVEDIR "libs") # Contains the static (*.a) and import libraries (*.lib)
set(ARCHIVEDIR "lib") # Contains the static (*.a) and import libraries (*.lib)

# Directories specific to 'libpython'
set(LIBPYTHON_LIBDIR ${LIBDIR})
Expand All @@ -555,6 +585,9 @@ if(UNIX)
set(PYCONFIG_BUILD_DIR ${BIN_BUILD_DIR})
configure_file(cmake/config-unix/pyconfig.h.in
${PYCONFIG_BUILD_DIR}/pyconfig.h)
file(GENERATE
OUTPUT $<TARGET_FILE_DIR:python>/pyconfig.h
INPUT ${PYCONFIG_BUILD_DIR}/pyconfig.h)
elseif(WIN32)
set(PYCONFIG_BUILD_DIR ${SRC_DIR}/PC) # In a windows build tree, 'pyconfig.h' is NOT required to
# live along side the python executable.
Expand Down Expand Up @@ -625,7 +658,11 @@ if(BUILD_LIBPYTHON_SHARED AND UNIX)
if(APPLE)
set(_envvar DYLD_LIBRARY_PATH)
endif()
set(PYTHON_WRAPPER_COMMAND env ${_envvar}=${PROJECT_BINARY_DIR}/${LIBPYTHON_LIBDIR})
set(PYTHON_HOME_PATH )
if(BUILD_FRAMEWORK)
set(PYTHON_HOME_PATH PYTHONHOME=${PROJECT_BINARY_DIR}/${PYTHONHOME} PYTHONPATH=${PROJECT_BINARY_DIR}/${PYTHONHOME})
endif()
set(PYTHON_WRAPPER_COMMAND env ${_envvar}=${PROJECT_BINARY_DIR}/${LIBPYTHON_LIBDIR} ${PYTHON_HOME_PATH})
endif()

# Add extension modules
Expand Down Expand Up @@ -658,7 +695,7 @@ endif()

# Add target to run "Argument Clinic" over all source files
add_custom_target(clinic
COMMAND python ${SRC_DIR}/Tools/clinic/clinic.py --make
COMMAND python -B ${SRC_DIR}/Tools/clinic/clinic.py --make
DEPENDS python
WORKING_DIRECTORY ${SRC_DIR}
COMMENT "Running 'Argument Clinic' over all source files"
Expand Down Expand Up @@ -726,7 +763,7 @@ endif()

# Add target to generate 'opcode.h' header file
add_custom_target(generate_opcode_h
COMMAND python ${SRC_DIR}/Tools/scripts/generate_opcode_h.py
COMMAND python -B ${SRC_DIR}/Tools/scripts/generate_opcode_h.py
${SRC_DIR}/Lib/opcode.py
${PROJECT_BINARY_DIR}/CMakeFiles/opcode.h
COMMAND ${CMAKE_COMMAND} -E copy_if_different
Expand All @@ -740,7 +777,7 @@ add_custom_target(generate_opcode_h

# Add target to generate 'Include/Python-ast.h' from 'Python.asdl'
add_custom_target(generate_python_ast_h
COMMAND python ${SRC_DIR}/Parser/asdl_c.py
COMMAND python -B ${SRC_DIR}/Parser/asdl_c.py
-h ${SRC_DIR}/Include
${SRC_DIR}/Parser/Python.asdl
DEPENDS python
Expand All @@ -751,7 +788,7 @@ add_custom_target(generate_python_ast_h

# Add target to generate 'Python/Python-ast.c' from 'Python.asdl'
add_custom_target(generate_python_ast_c
COMMAND python ${SRC_DIR}/Parser/asdl_c.py
COMMAND python -B ${SRC_DIR}/Parser/asdl_c.py
-c ${SRC_DIR}/Python
${SRC_DIR}/Parser/Python.asdl
DEPENDS python
Expand Down Expand Up @@ -793,19 +830,22 @@ if(UNIX)
${PROJECT_BINARY_DIR}/Misc/python-${LIBPYTHON_VERSION}.pc
${PROJECT_BINARY_DIR}/Misc/python-${PY_VERSION_MAJOR}.pc
${PROJECT_BINARY_DIR}/Misc/python.pc
DESTINATION lib/pkgconfig
DESTINATION ${LIBDIR}/pkgconfig
COMPONENT Development)
endif()

# Makefile
set(MAKEFILE_LDSHARED_FLAGS "-shared")
if(APPLE)
if(APPLE OR ANDROID)
set(MAKEFILE_LDSHARED_FLAGS "-dynamiclib -headerpad_max_install_names -undefined dynamic_lookup")
endif()
configure_file(cmake/makefile-variables.in
${BIN_BUILD_DIR}/Makefile @ONLY)
${BIN_BUILD_DIR}/Makefile.in @ONLY)
file(GENERATE
OUTPUT $<TARGET_FILE_DIR:python>/Makefile
INPUT ${BIN_BUILD_DIR}/Makefile.in)
if(INSTALL_DEVELOPMENT)
install(FILES ${BIN_BUILD_DIR}/Makefile
install(FILES $<TARGET_FILE_DIR:python>/Makefile
DESTINATION ${LIB_CONFIG_INSTALL_DIR}
RENAME Makefile
COMPONENT Development)
Expand All @@ -820,7 +860,7 @@ if(UNIX)

# Install manual
if(INSTALL_MANUAL)
set(_install_man FILES ${SRC_DIR}/Misc/python.man DESTINATION share/man/man1 COMPONENT Runtime)
set(_install_man FILES ${SRC_DIR}/Misc/python.man DESTINATION ${SHARE_INSTALL_DIR}/man/man1 COMPONENT Runtime)
install(${_install_man} RENAME python${LIBPYTHON_VERSION}.1)
install(${_install_man} RENAME python${PY_VERSION_MAJOR}.1)
endif()
Expand Down Expand Up @@ -848,7 +888,7 @@ if(BUILD_TESTING)
)
set(TESTPROG ${PROJECT_BINARY_DIR}/${PYTHONHOME}/test/regrtest.py)
set(TESTPYTHONOPTS )
set(TESTPYTHON $<TARGET_FILE:python> ${TESTPYTHONOPTS})
set(TESTPYTHON $<TARGET_FILE:python> -B ${TESTPYTHONOPTS})
set(TESTPYTHON ${CMAKE_CROSSCOMPILING_EMULATOR} ${TESTPYTHON})
include(cmake/UnitTests.cmake)
foreach(unittest ${unittests})
Expand Down Expand Up @@ -919,18 +959,6 @@ write_basic_package_version_file(
COMPATIBILITY SameMajorVersion
)

if(INSTALL_DEVELOPMENT)
# Install 'PythonTargets.cmake', 'PythonConfig.cmake' and 'PythonConfigVersion.cmake
install(EXPORT PythonTargets
FILE PythonTargets.cmake
DESTINATION ${CONFIG_INSTALL_DIR} COMPONENT Development)

install(
FILES ${python_install_config} ${python_config_version}
DESTINATION ${CONFIG_INSTALL_DIR} COMPONENT Development
)
endif()

# Install License
set(license_destination ${PYTHONHOME})
if(INSTALL_WINDOWS_TRADITIONAL)
Expand Down
92 changes: 38 additions & 54 deletions cmake/ConfigureChecks.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,11 @@ check_include_files(grp.h HAVE_GRP_H)
check_include_files(ieeefp.h HAVE_IEEEFP_H)
check_include_files(inttypes.h HAVE_INTTYPES_H) # libffi and cpython
check_include_files(io.h HAVE_IO_H)
check_include_files(langinfo.h HAVE_LANGINFO_H)
if (ANDROID)
set(HAVE_LANGINFO_H 0) # Android cann't link functions from langinfo.h
else()
check_include_files(langinfo.h HAVE_LANGINFO_H)
endif()
check_include_files(libintl.h HAVE_LIBINTL_H)
check_include_files(libutil.h HAVE_LIBUTIL_H)
check_include_files(linux/tipc.h HAVE_LINUX_TIPC_H)
Expand Down Expand Up @@ -532,7 +536,7 @@ find_library(HAVE_LIBTERMCAP termcap)
set(LIBUTIL_LIBRARIES )
set(LIBUTIL_EXPECTED 1)

if(CMAKE_SYSTEM MATCHES "VxWorks\\-7$")
if(ANDROID OR CMAKE_SYSTEM MATCHES "VxWorks\\-7$")
set(LIBUTIL_EXPECTED 0)
set(HAVE_LIBUTIL 0)
endif()
Expand Down Expand Up @@ -907,7 +911,9 @@ set(WITH_DYLD 0)
set(WITH_NEXT_FRAMEWORK 0)
if(APPLE)
set(WITH_DYLD 1)
set(WITH_NEXT_FRAMEWORK 0) # TODO: See --enable-framework option.
if(BUILD_FRAMEWORK)
set(WITH_NEXT_FRAMEWORK 1) # TODO: See --enable-framework option.
endif()
endif()
set(PYTHONFRAMEWORK "")

Expand Down Expand Up @@ -1298,60 +1304,38 @@ python_platform_test(
# Check for various properties of floating point
#
#######################################################################

# Check whether C doubles are little-endian IEEE 754 binary64
set(check_src ${PROJECT_BINARY_DIR}/CMakeFiles/ac_cv_little_endian_double.c)
file(WRITE ${check_src} "#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, \"\\x05\\x04\\x03\\x02\\x01\\xff\\x3f\\x43\", 8) == 0)
return 0;
else
return 1;
}
")
python_platform_test_run(
DOUBLE_IS_LITTLE_ENDIAN_IEEE754
"Checking whether C doubles are little-endian IEEE 754 binary64"
${check_src}
DIRECT
)

# Check whether C doubles are big-endian IEEE 754 binary64
set(check_src ${PROJECT_BINARY_DIR}/CMakeFiles/ac_cv_big_endian_double.c)
file(WRITE ${check_src} "#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, \"\\x43\\x3f\\xff\\x01\\x02\\x03\\x04\\x05\", 8) == 0)
return 0;
else
return 1;
}
file(WRITE ${check_src} "
double d = 90904234967036810337470478905505011476211692735615632014797120844053488865816695273723469097858056257517020191247487429516932130503560650002327564517570778480236724525140520121371739201496540132640109977779420565776568942592.0;
")
python_platform_test_run(
DOUBLE_IS_BIG_ENDIAN_IEEE754
"Checking whether C doubles are big-endian IEEE 754 binary64"
${check_src}
DIRECT
)

# Check whether C doubles are ARM mixed-endian IEEE 754 binary64
set(check_src ${PROJECT_BINARY_DIR}/CMakeFiles/ac_cv_mixed_endian_double.c)
file(WRITE ${check_src} "#include <string.h>
int main() {
double x = 9006104071832581.0;
if (memcmp(&x, \"\\x01\\xff\\x3f\\x43\\x05\\x04\\x03\\x02\", 8) == 0)
return 0;
else
return 1;
}
")
python_platform_test_run(
DOUBLE_IS_ARM_MIXED_ENDIAN_IEEE754
"Checking doubles are ARM mixed-endian IEEE 754 binary64"
${check_src}
DIRECT
)
# TODO: factorize this try_compile statement
try_compile(DOUBLE_BIG_ENDIAN_TEST_COMPILED
${CMAKE_CURRENT_BINARY_DIR}
${check_src}
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
COMPILE_DEFINITIONS ${CMAKE_REQUIRED_DEFINITIONS}
CMAKE_FLAGS -DCOMPILE_DEFINITIONS:STRING=${MACRO_CHECK_FUNCTION_DEFINITIONS}
"${CHECK_C_SOURCE_COMPILES_ADD_LIBRARIES}"
"${CHECK_C_SOURCE_COMPILES_ADD_INCLUDES}"
COPY_FILE ${CMAKE_CURRENT_BINARY_DIR}/double_big_endian.bin)

if(DOUBLE_BIG_ENDIAN_TEST_COMPILED)
file(READ ${CMAKE_CURRENT_BINARY_DIR}/double_big_endian.bin DOUBLE_BIG_ENDIAN_DATA)
string(FIND ${DOUBLE_BIG_ENDIAN_DATA} "noonsees" NOONSEES)
if(NOONSEES)
set(DOUBLE_IS_BIG_ENDIAN_IEEE754 1)
set(DOUBLE_IS_LITTLE_ENDIAN_IEEE754 0)
else()
string(FIND ${DOUBLE_BIG_ENDIAN_DATA} "seesnoon" SEESNOON)
if(SEESNOON)
set(DOUBLE_IS_BIG_ENDIAN_IEEE754 0)
set(DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1)
else()
message(WARNING "Could not determine if double precision floats endianness")
endif()
endif()
endif()

# The short float repr introduced in Python 3.1 requires the
# correctly-rounded string <-> double conversion functions from
Expand Down
Loading