From c6f3e0d44157268c85b7fd45b25c8990b20ddf4e Mon Sep 17 00:00:00 2001 From: Anthony Shoumikhin Date: Fri, 28 Aug 2026 21:45:18 -0700 Subject: [PATCH 1/2] Export an MLX model for the iOS demo build The mv3 demo app is gaining an MLX option, and its Xcode project bundles mv3_mlx.pte, so this job has to produce that file or the build fails on a missing resource. MLX has no examples/ export script of its own yet, so lower it inline the same way the script already drives the portable, Core ML and XNNPACK exports. Lowering is ahead of time: neither the partitioner nor the preprocess step imports the mlx runtime package, so this runs on the existing runner without new dependencies. --- .ci/scripts/test_ios_ci.sh | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.ci/scripts/test_ios_ci.sh b/.ci/scripts/test_ios_ci.sh index a160bceed62..17471f2bba6 100755 --- a/.ci/scripts/test_ios_ci.sh +++ b/.ci/scripts/test_ios_ci.sh @@ -49,6 +49,26 @@ python3 -m examples.portable.scripts.export --model_name="$MODEL_NAME" --segment python3 -m examples.apple.coreml.scripts.export --model_name="$MODEL_NAME" python3 -m examples.xnnpack.aot_compiler --model_name="$MODEL_NAME" --delegate +# MLX has no examples/ export script of its own yet. Lowering is ahead of time and +# imports no mlx runtime package, so it runs on this runner like the others. +python3 - < Date: Sat, 29 Aug 2026 04:15:01 -0700 Subject: [PATCH 2/2] Stage the MLX model in both iOS scripts, and check it was produced Review found four things. There is a second staging path, scripts/test_ios.sh, that clones the same demo and moves the same wildcard, so it needs the same export or the demo fails on a missing build input once it requires the resource. The MPS removal edited both scripts in one commit; do the same here. save_pte_program logs write failures rather than raising, and `mv $MODEL_NAME*.pte` would still stage a truncated file, so assert the result is nonempty. Verified the guard fires for a missing file and for a zero-byte one, and passes otherwise. The heredoc marker was unquoted, the only one of its kind here: the other python heredoc in this directory uses 'PY'. The shell was rewriting the python body, so a future dollar sign or backtick would be eaten silently. Quote the marker and read the model name from argv. The lowering took the default edge compile config while all three sibling exports disable dim order for a stated iOS reason. Match them. --- .ci/scripts/test_ios_ci.sh | 23 +++++++++++++++++------ scripts/test_ios.sh | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/.ci/scripts/test_ios_ci.sh b/.ci/scripts/test_ios_ci.sh index 17471f2bba6..7a64d6ae241 100755 --- a/.ci/scripts/test_ios_ci.sh +++ b/.ci/scripts/test_ios_ci.sh @@ -51,23 +51,34 @@ python3 -m examples.xnnpack.aot_compiler --model_name="$MODEL_NAME" --delegate # MLX has no examples/ export script of its own yet. Lowering is ahead of time and # imports no mlx runtime package, so it runs on this runner like the others. -python3 - <&2 + exit 1 +} mkdir -p "$APP_PATH/Resources/Models/MobileNet/" mv $MODEL_NAME*.pte "$APP_PATH/Resources/Models/MobileNet/" diff --git a/scripts/test_ios.sh b/scripts/test_ios.sh index 0c6cf85652b..4145bd2a45f 100755 --- a/scripts/test_ios.sh +++ b/scripts/test_ios.sh @@ -66,6 +66,37 @@ python3 -m examples.portable.scripts.export --model_name="$MODEL_NAME" python3 -m examples.apple.coreml.scripts.export --model_name="$MODEL_NAME" python3 -m examples.xnnpack.aot_compiler --model_name="$MODEL_NAME" --delegate +# MLX has no examples/ export script of its own yet. Lowering is ahead of time and +# imports no mlx runtime package, so it runs on this runner like the others. +python3 - "$MODEL_NAME" <<'PY' +import sys + +import torch +from executorch.backends.mlx import MLXPartitioner +from executorch.examples.models import MODEL_NAME_TO_MODEL +from executorch.examples.models.model_factory import EagerModelFactory +from executorch.exir import EdgeCompileConfig, to_edge_transform_and_lower +from executorch.extension.export_util.utils import save_pte_program + +model_name = sys.argv[1] +model, example_inputs, _, _ = EagerModelFactory.create_model( + *MODEL_NAME_TO_MODEL[model_name] +) +program = to_edge_transform_and_lower( + torch.export.export(model.eval(), example_inputs), + partitioner=[MLXPartitioner()], + compile_config=EdgeCompileConfig(_skip_dim_order=True), +).to_executorch() +save_pte_program(program, f"{model_name}_mlx") +PY + +# save_pte_program logs write failures instead of raising, and the wildcard move +# below would happily stage a truncated file, so check the result here. +test -s "${MODEL_NAME}_mlx.pte" || { + echo "error: ${MODEL_NAME}_mlx.pte was not produced" >&2 + exit 1 +} + mkdir -p "$APP_PATH/Resources/Models/MobileNet/" mv $MODEL_NAME*.pte "$APP_PATH/Resources/Models/MobileNet/"