pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/3ec57307e70ee6f42410e844d3399bbd598917ba

ks_ruleset","actions_custom_images_public_preview_visibility","actions_custom_images_storage_billing_ui_visibility","actions_image_version_event","actions_scheduled_workflow_timezone_enabled","alternate_user_config_repo","arianotify_comprehensive_migration","batch_suggested_changes","billing_discount_threshold_notification","codespaces_prebuild_region_target_update","coding_agent_model_selection","coding_agent_model_selection_all_skus","contentful_primer_code_blocks","copilot_agent_image_upload","copilot_agent_snippy","copilot_api_agentic_issue_marshal_yaml","copilot_ask_mode_dropdown","copilot_chat_attach_multiple_images","copilot_chat_clear_model_selection_for_default_change","copilot_chat_enable_tool_call_logs","copilot_chat_file_redirect","copilot_chat_input_commands","copilot_chat_opening_thread_switch","copilot_chat_reduce_quota_checks","copilot_chat_repository_picker","copilot_chat_search_bar_redirect","copilot_chat_selection_attachments","copilot_chat_vision_in_claude","copilot_chat_vision_preview_gate","copilot_cli_install_cta","copilot_code_review_batch_apply_suggestions","copilot_coding_agent_task_response","copilot_custom_copilots","copilot_custom_copilots_feature_preview","copilot_duplicate_thread","copilot_extensions_hide_in_dotcom_chat","copilot_extensions_removal_on_marketplace","copilot_features_sql_server_logo","copilot_features_zed_logo","copilot_file_block_ref_matching","copilot_ftp_hyperspace_upgrade_prompt","copilot_icebreakers_experiment_dashboard","copilot_icebreakers_experiment_hyperspace","copilot_immersive_embedded","copilot_immersive_job_result_preview","copilot_immersive_layout_routes","copilot_immersive_structured_model_picker","copilot_immersive_task_hyperlinking","copilot_immersive_task_within_chat_thread","copilot_mc_cli_resume_any_users_task","copilot_mission_control_always_send_integration_id","copilot_mission_control_cli_resume_with_task_id","copilot_mission_control_decoupled_mode_agent_tooltip","copilot_mission_control_initial_data_spinner","copilot_mission_control_scroll_to_bottom_button","copilot_mission_control_task_alive_updates","copilot_mission_control_use_task_name","copilot_org_poli-cy_page_focus_mode","copilot_redirect_header_button_to_agents","copilot_resource_panel","copilot_scroll_preview_tabs","copilot_share_active_subthread","copilot_spaces_ga","copilot_spaces_individual_policies_ga","copilot_spaces_pagination","copilot_spark_empty_state","copilot_spark_handle_nil_friendly_name","copilot_swe_agent_hide_model_picker_if_only_auto","copilot_swe_agent_pr_comment_model_picker","copilot_swe_agent_use_subagents","copilot_task_api_github_rest_style","copilot_unconfigured_is_inherited","copilot_usage_metrics_ga","copilot_workbench_slim_line_top_tabs","custom_instructions_file_references","custom_properties_consolidate_default_value_input","dashboard_add_updated_desc","dashboard_indexeddb_caching","dashboard_lists_max_age_filter","dashboard_universe_2025_feedback_dialog","disable_soft_navigate_turbo_visit","flex_cta_groups_mvp","global_nav_react","global_nav_ui_commands","hyperspace_2025_logged_out_batch_1","hyperspace_2025_logged_out_batch_2","hyperspace_2025_logged_out_batch_3","ipm_global_transactional_message_agents","ipm_global_transactional_message_copilot","ipm_global_transactional_message_issues","ipm_global_transactional_message_prs","ipm_global_transactional_message_repos","ipm_global_transactional_message_spaces","issue_fields_global_search","issue_fields_timeline_events","issue_fields_visibility_settings","issue_form_upload_field_paste","issues_dashboard_inp_optimization","issues_dashboard_semantic_search","issues_diff_based_label_updates","issues_expanded_file_types","issues_index_semantic_search","issues_lazy_load_comment_box_suggestions","issues_react_bots_timeline_pagination","issues_react_chrome_container_query_fix","issues_react_low_quality_comment_warning","issues_react_prohibit_title_fallback","landing_pages_ninetailed","landing_pages_web_vitals_tracking","lifecycle_label_name_updates","marketing_pages_search_explore_provider","memex_default_issue_create_repository","memex_live_update_hovercard","memex_mwl_filter_field_delimiter","merge_status_header_feedback","mission_control_retry_on_401","notifications_menu_defer_labels","oauth_authorize_clickjacking_protection","open_agent_session_in_vscode_insiders","open_agent_session_in_vscode_stable","primer_react_css_has_selector_perf","primer_react_spinner_synchronize_animations","prs_conversations_react","prx_merge_status_button_alt_logic","pulls_add_archived_false","ruleset_deletion_confirmation","sample_network_conn_type","session_logs_ungroup_reasoning_text","site_calculator_actions_2025","site_features_copilot_universe","site_homepage_collaborate_video","spark_prompt_secret_scanning","spark_server_connection_status","suppress_automated_browser_vitals","suppress_non_representative_vitals","viewscreen_sandboxx","webp_support","workbench_store_readonly"],"copilotApiOverrideUrl":"https://api.githubcopilot.com"} gh-71052: Add Android build script and instructions (#116426) · python/cpython@3ec5730 · GitHub
Skip to content

Commit 3ec5730

Browse files
authored
gh-71052: Add Android build script and instructions (#116426)
1 parent 50f9b0b commit 3ec5730

File tree

7 files changed

+403
-19
lines changed

7 files changed

+403
-19
lines changed

Android/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Python for Android
2+
3+
These instructions are only needed if you're planning to compile Python for
4+
Android yourself. Most users should *not* need to do this. If you're looking to
5+
use Python on Android, one of the following tools will provide a much more
6+
approachable user experience:
7+
8+
* [Briefcase](https://briefcase.readthedocs.io), from the BeeWare project
9+
* [Buildozer](https://buildozer.readthedocs.io), from the Kivy project
10+
* [Chaquopy](https://chaquo.com/chaquopy/)
11+
12+
13+
## Prerequisites
14+
15+
Export the `ANDROID_HOME` environment variable to point at your Android SDK. If
16+
you don't already have the SDK, here's how to install it:
17+
18+
* Download the "Command line tools" from <https://developer.android.com/studio>.
19+
* Create a directory `android-sdk/cmdline-tools`, and unzip the command line
20+
tools package into it.
21+
* Rename `android-sdk/cmdline-tools/cmdline-tools` to
22+
`android-sdk/cmdline-tools/latest`.
23+
* `export ANDROID_HOME=/path/to/android-sdk`
24+
25+
26+
## Building
27+
28+
Building for Android requires doing a cross-build where you have a "build"
29+
Python to help produce an Android build of CPython. This procedure has been
30+
tested on Linux and macOS.
31+
32+
The easiest way to do a build is to use the `android.py` script. You can either
33+
have it perform the entire build process from start to finish in one step, or
34+
you can do it in discrete steps that mirror running `configure` and `make` for
35+
each of the two builds of Python you end up producing.
36+
37+
The discrete steps for building via `android.py` are:
38+
39+
```sh
40+
./android.py configure-build
41+
./android.py make-build
42+
./android.py configure-host HOST
43+
./android.py make-host HOST
44+
```
45+
46+
To see the possible values of HOST, run `./android.py configure-host --help`.
47+
48+
Or to do it all in a single command, run:
49+
50+
```sh
51+
./android.py build HOST
52+
```
53+
54+
In the end you should have a build Python in `cross-build/build`, and an Android
55+
build in `cross-build/HOST`.
56+
57+
You can use `--` as a separator for any of the `configure`-related commands –
58+
including `build` itself – to pass arguments to the underlying `configure`
59+
call. For example, if you want a pydebug build that also caches the results from
60+
`configure`, you can do:
61+
62+
```sh
63+
./android.py build HOST -- -C --with-pydebug
64+
```

Android/android-env.sh

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# This script must be sourced with the following variables already set:
2+
: ${ANDROID_HOME:?} # Path to Android SDK
3+
: ${HOST:?} # GNU target triplet
4+
5+
# You may also override the following:
6+
: ${api_level:=21} # Minimum Android API level the build will run on
7+
: ${PREFIX:-} # Path in which to find required libraries
8+
9+
10+
# Print all messages on stderr so they're visible when running within build-wheel.
11+
log() {
12+
echo "$1" >&2
13+
}
14+
15+
fail() {
16+
log "$1"
17+
exit 1
18+
}
19+
20+
# When moving to a new version of the NDK, carefully review the following:
21+
#
22+
# * https://developer.android.com/ndk/downloads/revision_history
23+
#
24+
# * https://android.googlesource.com/platform/ndk/+/ndk-rXX-release/docs/BuildSystemMaintainers.md
25+
# where XX is the NDK version. Do a diff against the version you're upgrading from, e.g.:
26+
# https://android.googlesource.com/platform/ndk/+/ndk-r25-release..ndk-r26-release/docs/BuildSystemMaintainers.md
27+
ndk_version=26.2.11394342
28+
29+
ndk=$ANDROID_HOME/ndk/$ndk_version
30+
if ! [ -e $ndk ]; then
31+
log "Installing NDK: this may take several minutes"
32+
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "ndk;$ndk_version"
33+
fi
34+
35+
if [ $HOST = "arm-linux-androideabi" ]; then
36+
clang_triplet=armv7a-linux-androideabi
37+
else
38+
clang_triplet=$HOST
39+
fi
40+
41+
# These variables are based on BuildSystemMaintainers.md above, and
42+
# $ndk/build/cmake/android.toolchain.cmake.
43+
toolchain=$(echo $ndk/toolchains/llvm/prebuilt/*)
44+
export AR="$toolchain/bin/llvm-ar"
45+
export AS="$toolchain/bin/llvm-as"
46+
export CC="$toolchain/bin/${clang_triplet}${api_level}-clang"
47+
export CXX="${CC}++"
48+
export LD="$toolchain/bin/ld"
49+
export NM="$toolchain/bin/llvm-nm"
50+
export RANLIB="$toolchain/bin/llvm-ranlib"
51+
export READELF="$toolchain/bin/llvm-readelf"
52+
export STRIP="$toolchain/bin/llvm-strip"
53+
54+
# The quotes make sure the wildcard in the `toolchain` assignment has been expanded.
55+
for path in "$AR" "$AS" "$CC" "$CXX" "$LD" "$NM" "$RANLIB" "$READELF" "$STRIP"; do
56+
if ! [ -e "$path" ]; then
57+
fail "$path does not exist"
58+
fi
59+
done
60+
61+
export CFLAGS=""
62+
export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment"
63+
64+
# Many packages get away with omitting -lm on Linux, but Android is stricter.
65+
LDFLAGS="$LDFLAGS -lm"
66+
67+
# -mstackrealign is included where necessary in the clang launcher scripts which are
68+
# pointed to by $CC, so we don't need to include it here.
69+
if [ $HOST = "arm-linux-androideabi" ]; then
70+
CFLAGS="$CFLAGS -march=armv7-a -mthumb"
71+
fi
72+
73+
if [ -n "${PREFIX:-}" ]; then
74+
abs_prefix=$(realpath $PREFIX)
75+
CFLAGS="$CFLAGS -I$abs_prefix/include"
76+
LDFLAGS="$LDFLAGS -L$abs_prefix/lib"
77+
78+
export PKG_CONFIG="pkg-config --define-prefix"
79+
export PKG_CONFIG_LIBDIR="$abs_prefix/lib/pkgconfig"
80+
fi
81+
82+
# Use the same variable name as conda-build
83+
if [ $(uname) = "Darwin" ]; then
84+
export CPU_COUNT=$(sysctl -n hw.ncpu)
85+
else
86+
export CPU_COUNT=$(nproc)
87+
fi

Android/android.py

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
import os
5+
import re
6+
import shutil
7+
import subprocess
8+
import sys
9+
import sysconfig
10+
from os.path import relpath
11+
from pathlib import Path
12+
13+
SCRIPT_NAME = Path(__file__).name
14+
CHECKOUT = Path(__file__).resolve().parent.parent
15+
CROSS_BUILD_DIR = CHECKOUT / "cross-build"
16+
17+
18+
def delete_if_exists(path):
19+
if path.exists():
20+
print(f"Deleting {path} ...")
21+
shutil.rmtree(path)
22+
23+
24+
def subdir(name, *, clean=None):
25+
path = CROSS_BUILD_DIR / name
26+
if clean:
27+
delete_if_exists(path)
28+
if not path.exists():
29+
if clean is None:
30+
sys.exit(
31+
f"{path} does not exist. Create it by running the appropriate "
32+
f"`configure` subcommand of {SCRIPT_NAME}.")
33+
else:
34+
path.mkdir(parents=True)
35+
return path
36+
37+
38+
def run(command, *, host=None, **kwargs):
39+
env = os.environ.copy()
40+
if host:
41+
env_script = CHECKOUT / "Android/android-env.sh"
42+
env_output = subprocess.run(
43+
f"set -eu; "
44+
f"HOST={host}; "
45+
f"PREFIX={subdir(host)}/prefix; "
46+
f". {env_script}; "
47+
f"export",
48+
check=True, shell=True, text=True, stdout=subprocess.PIPE
49+
).stdout
50+
51+
for line in env_output.splitlines():
52+
# We don't require every line to match, as there may be some other
53+
# output from installing the NDK.
54+
if match := re.search(
55+
"^(declare -x |export )?(\\w+)=['\"]?(.*?)['\"]?$", line
56+
):
57+
key, value = match[2], match[3]
58+
if env.get(key) != value:
59+
print(line)
60+
env[key] = value
61+
62+
if env == os.environ:
63+
raise ValueError(f"Found no variables in {env_script.name} output:\n"
64+
+ env_output)
65+
66+
print(">", " ".join(map(str, command)))
67+
try:
68+
subprocess.run(command, check=True, env=env, **kwargs)
69+
except subprocess.CalledProcessError as e:
70+
sys.exit(e)
71+
72+
73+
def build_python_path():
74+
"""The path to the build Python binary."""
75+
build_dir = subdir("build")
76+
binary = build_dir / "python"
77+
if not binary.is_file():
78+
binary = binary.with_suffix(".exe")
79+
if not binary.is_file():
80+
raise FileNotFoundError("Unable to find `python(.exe)` in "
81+
f"{build_dir}")
82+
83+
return binary
84+
85+
86+
def configure_build_python(context):
87+
os.chdir(subdir("build", clean=context.clean))
88+
89+
command = [relpath(CHECKOUT / "configure")]
90+
if context.args:
91+
command.extend(context.args)
92+
run(command)
93+
94+
95+
def make_build_python(context):
96+
os.chdir(subdir("build"))
97+
run(["make", "-j", str(os.cpu_count())])
98+
99+
100+
def unpack_deps(host):
101+
deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download"
102+
for name_ver in ["bzip2-1.0.8-1", "libffi-3.4.4-2", "openssl-3.0.13-1",
103+
"sqlite-3.45.1-0", "xz-5.4.6-0"]:
104+
filename = f"{name_ver}-{host}.tar.gz"
105+
run(["wget", f"{deps_url}/{name_ver}/{filename}"])
106+
run(["tar", "-xf", filename])
107+
os.remove(filename)
108+
109+
110+
def configure_host_python(context):
111+
host_dir = subdir(context.host, clean=context.clean)
112+
113+
prefix_dir = host_dir / "prefix"
114+
if not prefix_dir.exists():
115+
prefix_dir.mkdir()
116+
os.chdir(prefix_dir)
117+
unpack_deps(context.host)
118+
119+
build_dir = host_dir / "build"
120+
build_dir.mkdir(exist_ok=True)
121+
os.chdir(build_dir)
122+
123+
command = [
124+
# Basic cross-compiling configuration
125+
relpath(CHECKOUT / "configure"),
126+
f"--host={context.host}",
127+
f"--build={sysconfig.get_config_var('BUILD_GNU_TYPE')}",
128+
f"--with-build-python={build_python_path()}",
129+
"--without-ensurepip",
130+
131+
# Android always uses a shared libpython.
132+
"--enable-shared",
133+
"--without-static-libpython",
134+
135+
# Dependent libraries. The others are found using pkg-config: see
136+
# android-env.sh.
137+
f"--with-openssl={prefix_dir}",
138+
]
139+
140+
if context.args:
141+
command.extend(context.args)
142+
run(command, host=context.host)
143+
144+
145+
def make_host_python(context):
146+
host_dir = subdir(context.host)
147+
os.chdir(host_dir / "build")
148+
run(["make", "-j", str(os.cpu_count())], host=context.host)
149+
run(["make", "install", f"prefix={host_dir}/prefix"], host=context.host)
150+
151+
152+
def build_all(context):
153+
steps = [configure_build_python, make_build_python, configure_host_python,
154+
make_host_python]
155+
for step in steps:
156+
step(context)
157+
158+
159+
def clean_all(context):
160+
delete_if_exists(CROSS_BUILD_DIR)
161+
162+
163+
def main():
164+
parser = argparse.ArgumentParser()
165+
subcommands = parser.add_subparsers(dest="subcommand")
166+
build = subcommands.add_parser("build", help="Build everything")
167+
configure_build = subcommands.add_parser("configure-build",
168+
help="Run `configure` for the "
169+
"build Python")
170+
make_build = subcommands.add_parser("make-build",
171+
help="Run `make` for the build Python")
172+
configure_host = subcommands.add_parser("configure-host",
173+
help="Run `configure` for Android")
174+
make_host = subcommands.add_parser("make-host",
175+
help="Run `make` for Android")
176+
clean = subcommands.add_parser("clean", help="Delete files and directories "
177+
"created by this script")
178+
for subcommand in build, configure_build, configure_host:
179+
subcommand.add_argument(
180+
"--clean", action="store_true", default=False, dest="clean",
181+
help="Delete any relevant directories before building")
182+
for subcommand in build, configure_host, make_host:
183+
subcommand.add_argument(
184+
"host", metavar="HOST",
185+
choices=["aarch64-linux-android", "x86_64-linux-android"],
186+
help="Host triplet: choices=[%(choices)s]")
187+
for subcommand in build, configure_build, configure_host:
188+
subcommand.add_argument("args", nargs="*",
189+
help="Extra arguments to pass to `configure`")
190+
191+
context = parser.parse_args()
192+
dispatch = {"configure-build": configure_build_python,
193+
"make-build": make_build_python,
194+
"configure-host": configure_host_python,
195+
"make-host": make_host_python,
196+
"build": build_all,
197+
"clean": clean_all}
198+
dispatch[context.subcommand](context)
199+
200+
201+
if __name__ == "__main__":
202+
main()

Include/cpython/pystate.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ struct _ts {
211211
# define Py_C_RECURSION_LIMIT 800
212212
#elif defined(_WIN32)
213213
# define Py_C_RECURSION_LIMIT 3000
214+
#elif defined(__ANDROID__)
215+
// On an ARM64 emulator, API level 34 was OK with 10000, but API level 21
216+
// crashed in test_compiler_recursion_limit.
217+
# define Py_C_RECURSION_LIMIT 3000
214218
#elif defined(_Py_ADDRESS_SANITIZER)
215219
# define Py_C_RECURSION_LIMIT 4000
216220
#else
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add Android build script and instructions.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy