Skip to content

gh-131531: android.py enhancements to support cibuildwheel #132870

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

Merged
merged 18 commits into from
Jun 5, 2025
Merged
Changes from 1 commit
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
Next Next commit
Add android.py env command
  • Loading branch information
mhsmith committed Apr 24, 2025
commit 71cccb3a12ebcf7c79ce8bf695da13dae02a7be6
97 changes: 64 additions & 33 deletions Android/android.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@

SCRIPT_NAME = Path(__file__).name
ANDROID_DIR = Path(__file__).resolve().parent
CHECKOUT = ANDROID_DIR.parent
PYTHON_DIR = ANDROID_DIR.parent
in_source_tree = (
ANDROID_DIR.name == "Android" and (PYTHON_DIR / "pyconfig.h.in").exists()
)

TESTBED_DIR = ANDROID_DIR / "testbed"
CROSS_BUILD_DIR = CHECKOUT / "cross-build"
CROSS_BUILD_DIR = PYTHON_DIR / "cross-build"

HOSTS = ["aarch64-linux-android", "x86_64-linux-android"]
APP_ID = "org.python.testbed"
Expand Down Expand Up @@ -74,41 +78,62 @@ def subdir(*parts, create=False):

def run(command, *, host=None, env=None, log=True, **kwargs):
kwargs.setdefault("check", True)

if env is None:
env = os.environ.copy()
original_env = env.copy()

if host:
env_script = ANDROID_DIR / "android-env.sh"
env_output = subprocess.run(
f"set -eu; "
f"HOST={host}; "
f"PREFIX={subdir(host)}/prefix; "
f". {env_script}; "
f"export",
check=True, shell=True, text=True, stdout=subprocess.PIPE
).stdout

for line in env_output.splitlines():
# We don't require every line to match, as there may be some other
# output from installing the NDK.
if match := re.search(
"^(declare -x |export )?(\\w+)=['\"]?(.*?)['\"]?$", line
):
key, value = match[2], match[3]
if env.get(key) != value:
print(line)
env[key] = value

if env == original_env:
raise ValueError(f"Found no variables in {env_script.name} output:\n"
+ env_output)
env.update(android_env(host))

if log:
print(">", " ".join(map(str, command)))
return subprocess.run(command, env=env, **kwargs)


def print_env(context):
android_env(getattr(context, "host", None))


def android_env(host):
if host:
prefix = subdir(host) / "prefix"
else:
prefix = ANDROID_DIR / "prefix"
sysconfigdata_files = prefix.glob("lib/python*/_sysconfigdata__android_*.py")
host = re.fullmatch(
r"_sysconfigdata__android_(.+).py", next(sysconfigdata_files).name
)[1]

env_script = ANDROID_DIR / "android-env.sh"
env_output = subprocess.run(
f"set -eu; "
f"export HOST={host}; "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason this has become a full export?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HOST is a somewhat standardized way of indicating cross-compilation, e.g. it's used by conda-build. So it makes sense to expose it to the build system.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also using it in the cibuildwheel PR to set sys.implementation._multiarch in the cross-compilation environment.

f"PREFIX={prefix}; "
f". {env_script}; "
f"export",
check=True, shell=True, capture_output=True, text=True,
).stdout

env = {}
for line in env_output.splitlines():
# We don't require every line to match, as there may be some other
# output from installing the NDK.
if match := re.search(
"^(declare -x |export )?(\\w+)=['\"]?(.*?)['\"]?$", line
):
key, value = match[2], match[3]
if os.environ.get(key) != value:
env[key] = value

if not env:
raise ValueError(f"Found no variables in {env_script.name} output:\n"
+ env_output)

# Format the environment so it can be pasted into a shell.
for key, value in sorted(env.items()):
print(f"export {key}={shlex.quote(value)}")
return env


def build_python_path():
"""The path to the build Python binary."""
build_dir = subdir("build")
Expand All @@ -127,7 +152,7 @@ def configure_build_python(context):
clean("build")
os.chdir(subdir("build", create=True))

command = [relpath(CHECKOUT / "configure")]
command = [relpath(PYTHON_DIR / "configure")]
if context.args:
command.extend(context.args)
run(command)
Expand Down Expand Up @@ -168,7 +193,7 @@ def configure_host_python(context):
os.chdir(host_dir)
command = [
# Basic cross-compiling configuration
relpath(CHECKOUT / "configure"),
relpath(PYTHON_DIR / "configure"),
f"--host={context.host}",
f"--build={sysconfig.get_config_var('BUILD_GNU_TYPE')}",
f"--with-build-python={build_python_path()}",
Expand Down Expand Up @@ -624,8 +649,7 @@ def parse_args():
configure_build = subcommands.add_parser("configure-build",
help="Run `configure` for the "
"build Python")
make_build = subcommands.add_parser("make-build",
help="Run `make` for the build Python")
subcommands.add_parser("make-build", help="Run `make` for the build Python")
configure_host = subcommands.add_parser("configure-host",
help="Run `configure` for Android")
make_host = subcommands.add_parser("make-host",
Expand All @@ -637,16 +661,22 @@ def parse_args():
test = subcommands.add_parser(
"test", help="Run the test suite")
package = subcommands.add_parser("package", help="Make a release package")
env = subcommands.add_parser("env", help="Print environment variables")

# Common arguments
for subcommand in build, configure_build, configure_host:
subcommand.add_argument(
"--clean", action="store_true", default=False, dest="clean",
help="Delete the relevant build and prefix directories first")
for subcommand in [build, configure_host, make_host, package]:

host_commands = [build, configure_host, make_host, package]
if in_source_tree:
host_commands.append(env)
for subcommand in host_commands:
subcommand.add_argument(
"host", metavar="HOST", choices=HOSTS,
help="Host triplet: choices=[%(choices)s]")

for subcommand in build, configure_build, configure_host:
subcommand.add_argument("args", nargs="*",
help="Extra arguments to pass to `configure`")
Expand Down Expand Up @@ -690,6 +720,7 @@ def main():
"build-testbed": build_testbed,
"test": run_testbed,
"package": package,
"env": print_env,
}

try:
Expand Down
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

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