diff --git a/install_executorch.py b/install_executorch.py index 52ba89edd7d..bd65af88dc6 100644 --- a/install_executorch.py +++ b/install_executorch.py @@ -10,59 +10,15 @@ import glob import itertools import os -import platform -import re import shutil import subprocess import sys -# Before doing anything, cd to the directory containing this script. -os.chdir(os.path.dirname(os.path.abspath(__file__))) - - -def python_is_compatible(): - # Scrape the version range from pyproject.toml, which should be in the current directory. - version_specifier = None - with open("pyproject.toml", "r") as file: - for line in file: - if line.startswith("requires-python"): - match = re.search(r'"([^"]*)"', line) - if match: - version_specifier = match.group(1) - break - - if not version_specifier: - print( - "WARNING: Skipping python version check: version range not found", - file=sys.stderr, - ) - return False - - # Install the packaging module if necessary. - try: - import packaging - except ImportError: - subprocess.run( - [sys.executable, "-m", "pip", "install", "packaging"], check=True - ) - # Compare the current python version to the range in version_specifier. Exits - # with status 1 if the version is not compatible, or with status 0 if the - # version is compatible or the logic itself fails. - try: - import packaging.specifiers - import packaging.version - - python_version = packaging.version.parse(platform.python_version()) - version_range = packaging.specifiers.SpecifierSet(version_specifier) - if python_version not in version_range: - print( - f'ERROR: ExecuTorch does not support python version {python_version}: must satisfy "{version_specifier}"', - file=sys.stderr, - ) - return False - except Exception as e: - print(f"WARNING: Skipping python version check: {e}", file=sys.stderr) - return True +from install_requirements import ( + install_requirements, + python_is_compatible, + TORCH_NIGHTLY_URL, +) def clean(): @@ -79,78 +35,6 @@ def clean(): VALID_PYBINDS = ["coreml", "mps", "xnnpack"] -# The pip repository that hosts nightly torch packages. -TORCH_NIGHTLY_URL = "https://download.pytorch.org/whl/nightly/cpu" - - -# Since ExecuTorch often uses main-branch features of pytorch, only the nightly -# pip versions will have the required features. -# -# NOTE: If a newly-fetched version of the executorch repo changes the value of -# NIGHTLY_VERSION, you should re-run this script to install the necessary -# package versions. -NIGHTLY_VERSION = "dev20250104" - - -def install_requirements(use_pytorch_nightly): - # pip packages needed by exir. - EXIR_REQUIREMENTS = [ - # Setting use_pytorch_nightly to false to test the pinned PyTorch commit. Note - # that we don't need to set any version number there because they have already - # been installed on CI before this step, so pip won't reinstall them - f"torch==2.6.0.{NIGHTLY_VERSION}" if use_pytorch_nightly else "torch", - ( - f"torchvision==0.22.0.{NIGHTLY_VERSION}" - if use_pytorch_nightly - else "torchvision" - ), # For testing. - ] - - EXAMPLES_REQUIREMENTS = [ - f"torchaudio==2.6.0.{NIGHTLY_VERSION}" if use_pytorch_nightly else "torchaudio", - ] - - # Assemble the list of requirements to actually install. - # TODO: Add options for reducing the number of requirements. - REQUIREMENTS_TO_INSTALL = EXIR_REQUIREMENTS + EXAMPLES_REQUIREMENTS - - # Install the requirements. `--extra-index-url` tells pip to look for package - # versions on the provided URL if they aren't available on the default URL. - subprocess.run( - [ - sys.executable, - "-m", - "pip", - "install", - "-r", - "requirements-examples.txt", - *REQUIREMENTS_TO_INSTALL, - "--extra-index-url", - TORCH_NIGHTLY_URL, - ], - check=True, - ) - - LOCAL_REQUIREMENTS = [ - "third-party/ao", # We need the latest kernels for fast iteration, so not relying on pypi. - ] - - # Install packages directly from local copy instead of pypi. - # This is usually not recommended. - subprocess.run( - [ - sys.executable, - "-m", - "pip", - "install", - # Without --no-build-isolation, setup.py can't find the torch module. - "--no-build-isolation", - *LOCAL_REQUIREMENTS, - ], - check=True, - ) - - def main(args): if not python_is_compatible(): sys.exit(1) @@ -252,4 +136,9 @@ def main(args): if __name__ == "__main__": + # Before doing anything, cd to the directory containing this script. + os.chdir(os.path.dirname(os.path.abspath(__file__))) + if not python_is_compatible(): + sys.exit(1) + main(sys.argv[1:]) diff --git a/install_executorch.sh b/install_executorch.sh index d34bdfdfbad..ec8cad12266 100755 --- a/install_executorch.sh +++ b/install_executorch.sh @@ -7,19 +7,4 @@ # Before doing anything, cd to the directory containing this script. cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null || /bin/true - -# Find the names of the python tools to use. -if [[ -z $PYTHON_EXECUTABLE ]]; -then - if [[ -z $CONDA_DEFAULT_ENV ]] || [[ $CONDA_DEFAULT_ENV == "base" ]] || [[ ! -x "$(command -v python)" ]]; - then - PYTHON_EXECUTABLE=python3 - else - PYTHON_EXECUTABLE=python - fi -fi - -$PYTHON_EXECUTABLE ./install_executorch.py "$@" - -# Exit with the same status as the python script. -exit $? +./run_python_script.sh ./install_executorch.py "$@" diff --git a/install_requirements.py b/install_requirements.py new file mode 100644 index 00000000000..4450367ff49 --- /dev/null +++ b/install_requirements.py @@ -0,0 +1,150 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# Copyright 2024-25 Arm Limited and/or its affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +import argparse +import platform +import re +import subprocess +import sys + + +def python_is_compatible(): + # Scrape the version range from pyproject.toml, which should be in the current directory. + version_specifier = None + with open("pyproject.toml", "r") as file: + for line in file: + if line.startswith("requires-python"): + match = re.search(r'"([^"]*)"', line) + if match: + version_specifier = match.group(1) + break + + if not version_specifier: + print( + "WARNING: Skipping python version check: version range not found", + file=sys.stderr, + ) + return False + + # Install the packaging module if necessary. + try: + import packaging + except ImportError: + subprocess.run( + [sys.executable, "-m", "pip", "install", "packaging"], check=True + ) + # Compare the current python version to the range in version_specifier. Exits + # with status 1 if the version is not compatible, or with status 0 if the + # version is compatible or the logic itself fails. + try: + import packaging.specifiers + import packaging.version + + python_version = packaging.version.parse(platform.python_version()) + version_range = packaging.specifiers.SpecifierSet(version_specifier) + if python_version not in version_range: + print( + f'ERROR: ExecuTorch does not support python version {python_version}: must satisfy "{version_specifier}"', + file=sys.stderr, + ) + return False + except Exception as e: + print(f"WARNING: Skipping python version check: {e}", file=sys.stderr) + return True + + +# The pip repository that hosts nightly torch packages. +TORCH_NIGHTLY_URL = "https://download.pytorch.org/whl/nightly/cpu" + + +# Since ExecuTorch often uses main-branch features of pytorch, only the nightly +# pip versions will have the required features. +# +# NOTE: If a newly-fetched version of the executorch repo changes the value of +# NIGHTLY_VERSION, you should re-run this script to install the necessary +# package versions. +NIGHTLY_VERSION = "dev20250104" + + +def install_requirements(use_pytorch_nightly): + # pip packages needed by exir. + EXIR_REQUIREMENTS = [ + # Setting use_pytorch_nightly to false to test the pinned PyTorch commit. Note + # that we don't need to set any version number there because they have already + # been installed on CI before this step, so pip won't reinstall them + f"torch==2.6.0.{NIGHTLY_VERSION}" if use_pytorch_nightly else "torch", + ( + f"torchvision==0.22.0.{NIGHTLY_VERSION}" + if use_pytorch_nightly + else "torchvision" + ), # For testing. + ] + + EXAMPLES_REQUIREMENTS = [ + f"torchaudio==2.6.0.{NIGHTLY_VERSION}" if use_pytorch_nightly else "torchaudio", + ] + + # Assemble the list of requirements to actually install. + # TODO: Add options for reducing the number of requirements. + REQUIREMENTS_TO_INSTALL = EXIR_REQUIREMENTS + EXAMPLES_REQUIREMENTS + + # Install the requirements. `--extra-index-url` tells pip to look for package + # versions on the provided URL if they aren't available on the default URL. + subprocess.run( + [ + sys.executable, + "-m", + "pip", + "install", + "-r", + "requirements-examples.txt", + *REQUIREMENTS_TO_INSTALL, + "--extra-index-url", + TORCH_NIGHTLY_URL, + ], + check=True, + ) + + LOCAL_REQUIREMENTS = [ + "third-party/ao", # We need the latest kernels for fast iteration, so not relying on pypi. + ] + + # Install packages directly from local copy instead of pypi. + # This is usually not recommended. + subprocess.run( + [ + sys.executable, + "-m", + "pip", + "install", + # Without --no-build-isolation, setup.py can't find the torch module. + "--no-build-isolation", + *LOCAL_REQUIREMENTS, + ], + check=True, + ) + + +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument( + "--use-pt-pinned-commit", + action="store_true", + help="build from the pinned PyTorch commit instead of nightly", + ) + args = parser.parse_args(args) + install_requirements(use_pytorch_nightly=not bool(args.use_pt_pinned_commit)) + + +if __name__ == "__main__": + import os + + # Before doing anything, cd to the directory containing this script. + os.chdir(os.path.dirname(os.path.abspath(__file__))) + if not python_is_compatible(): + sys.exit(1) + main(sys.argv[1:]) diff --git a/install_requirements.sh b/install_requirements.sh new file mode 100755 index 00000000000..ef156cd020e --- /dev/null +++ b/install_requirements.sh @@ -0,0 +1,10 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Before doing anything, cd to the directory containing this script. +cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null || /bin/true +./run_python_script.sh ./install_requirements.py "$@" diff --git a/run_python_script.sh b/run_python_script.sh new file mode 100755 index 00000000000..4f9a74ec360 --- /dev/null +++ b/run_python_script.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Before doing anything, cd to the directory containing this script. +cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null || /bin/true + +# Find the names of the python tools to use. +if [[ -z $PYTHON_EXECUTABLE ]]; +then + if [[ -z $CONDA_DEFAULT_ENV ]] || [[ $CONDA_DEFAULT_ENV == "base" ]] || [[ ! -x "$(command -v python)" ]]; + then + PYTHON_EXECUTABLE=python3 + else + PYTHON_EXECUTABLE=python + fi +fi + +SCRIPT="$1"; shift +$PYTHON_EXECUTABLE $SCRIPT "$@" + +# Exit with the same status as the python script. +exit $?