xref: /aosp_15_r20/external/executorch/setup.py (revision 523fa7a60841cd1ecfb9cc4201f1ca8b03ed023a)
1*523fa7a6SAndroid Build Coastguard Worker# Copyright (c) Meta Platforms, Inc. and affiliates.
2*523fa7a6SAndroid Build Coastguard Worker# Copyright 2024 Arm Limited and/or its affiliates.
3*523fa7a6SAndroid Build Coastguard Worker# All rights reserved.
4*523fa7a6SAndroid Build Coastguard Worker#
5*523fa7a6SAndroid Build Coastguard Worker# This source code is licensed under the BSD-style license found in the
6*523fa7a6SAndroid Build Coastguard Worker# LICENSE file in the root directory of this source tree.
7*523fa7a6SAndroid Build Coastguard Worker
8*523fa7a6SAndroid Build Coastguard Worker# Part of this code is from pybind11 cmake_example:
9*523fa7a6SAndroid Build Coastguard Worker# https://github.com/pybind/cmake_example/blob/master/setup.py so attach the
10*523fa7a6SAndroid Build Coastguard Worker# license below.
11*523fa7a6SAndroid Build Coastguard Worker
12*523fa7a6SAndroid Build Coastguard Worker# Copyright (c) 2016 The Pybind Development Team, All rights reserved.
13*523fa7a6SAndroid Build Coastguard Worker#
14*523fa7a6SAndroid Build Coastguard Worker# Redistribution and use in source and binary forms, with or without
15*523fa7a6SAndroid Build Coastguard Worker# modification, are permitted provided that the following conditions are met:
16*523fa7a6SAndroid Build Coastguard Worker#
17*523fa7a6SAndroid Build Coastguard Worker# 1. Redistributions of source code must retain the above copyright notice, this
18*523fa7a6SAndroid Build Coastguard Worker#    list of conditions and the following disclaimer.
19*523fa7a6SAndroid Build Coastguard Worker#
20*523fa7a6SAndroid Build Coastguard Worker# 2. Redistributions in binary form must reproduce the above copyright notice,
21*523fa7a6SAndroid Build Coastguard Worker#    this list of conditions and the following disclaimer in the documentation
22*523fa7a6SAndroid Build Coastguard Worker#    and/or other materials provided with the distribution.
23*523fa7a6SAndroid Build Coastguard Worker#
24*523fa7a6SAndroid Build Coastguard Worker# 3. Neither the name of the copyright holder nor the names of its contributors
25*523fa7a6SAndroid Build Coastguard Worker#    may be used to endorse or promote products derived from this software
26*523fa7a6SAndroid Build Coastguard Worker#    without specific prior written permission.
27*523fa7a6SAndroid Build Coastguard Worker#
28*523fa7a6SAndroid Build Coastguard Worker# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
29*523fa7a6SAndroid Build Coastguard Worker# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
30*523fa7a6SAndroid Build Coastguard Worker# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
31*523fa7a6SAndroid Build Coastguard Worker# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
32*523fa7a6SAndroid Build Coastguard Worker# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33*523fa7a6SAndroid Build Coastguard Worker# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
34*523fa7a6SAndroid Build Coastguard Worker# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
35*523fa7a6SAndroid Build Coastguard Worker# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
36*523fa7a6SAndroid Build Coastguard Worker# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37*523fa7a6SAndroid Build Coastguard Worker# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38*523fa7a6SAndroid Build Coastguard Worker#
39*523fa7a6SAndroid Build Coastguard Worker# You are under no obligation whatsoever to provide any bug fixes, patches, or
40*523fa7a6SAndroid Build Coastguard Worker# upgrades to the features, functionality or performance of the source code
41*523fa7a6SAndroid Build Coastguard Worker# ("Enhancements") to anyone; however, if you choose to make your Enhancements
42*523fa7a6SAndroid Build Coastguard Worker# available either publicly, or directly to the author of this software, without
43*523fa7a6SAndroid Build Coastguard Worker# imposing a separate written license agreement for such Enhancements, then you
44*523fa7a6SAndroid Build Coastguard Worker# hereby grant the following license: a non-exclusive, royalty-free perpetual
45*523fa7a6SAndroid Build Coastguard Worker# license to install, use, modify, prepare derivative works, incorporate into
46*523fa7a6SAndroid Build Coastguard Worker# other computer software, distribute, and sublicense such enhancements or
47*523fa7a6SAndroid Build Coastguard Worker# derivative works thereof, in binary and source code form.
48*523fa7a6SAndroid Build Coastguard Worker
49*523fa7a6SAndroid Build Coastguard Workerimport contextlib
50*523fa7a6SAndroid Build Coastguard Workerimport os
51*523fa7a6SAndroid Build Coastguard Workerimport platform
52*523fa7a6SAndroid Build Coastguard Workerimport re
53*523fa7a6SAndroid Build Coastguard Workerimport sys
54*523fa7a6SAndroid Build Coastguard Worker
55*523fa7a6SAndroid Build Coastguard Worker# Import this before distutils so that setuptools can intercept the distuils
56*523fa7a6SAndroid Build Coastguard Worker# imports.
57*523fa7a6SAndroid Build Coastguard Workerimport setuptools  # noqa: F401 # usort: skip
58*523fa7a6SAndroid Build Coastguard Worker
59*523fa7a6SAndroid Build Coastguard Workerfrom distutils import log
60*523fa7a6SAndroid Build Coastguard Workerfrom distutils.sysconfig import get_python_lib
61*523fa7a6SAndroid Build Coastguard Workerfrom pathlib import Path
62*523fa7a6SAndroid Build Coastguard Workerfrom typing import List, Optional
63*523fa7a6SAndroid Build Coastguard Worker
64*523fa7a6SAndroid Build Coastguard Workerfrom setuptools import Extension, setup
65*523fa7a6SAndroid Build Coastguard Workerfrom setuptools.command.build import build
66*523fa7a6SAndroid Build Coastguard Workerfrom setuptools.command.build_ext import build_ext
67*523fa7a6SAndroid Build Coastguard Workerfrom setuptools.command.build_py import build_py
68*523fa7a6SAndroid Build Coastguard Worker
69*523fa7a6SAndroid Build Coastguard Worker# For information on setuptools Command subclassing see
70*523fa7a6SAndroid Build Coastguard Worker# https://setuptools.pypa.io/en/latest/userguide/extension.html
71*523fa7a6SAndroid Build Coastguard Worker
72*523fa7a6SAndroid Build Coastguard Worker
73*523fa7a6SAndroid Build Coastguard Workerclass ShouldBuild:
74*523fa7a6SAndroid Build Coastguard Worker    """Indicates whether to build various components."""
75*523fa7a6SAndroid Build Coastguard Worker
76*523fa7a6SAndroid Build Coastguard Worker    @staticmethod
77*523fa7a6SAndroid Build Coastguard Worker    def _is_env_enabled(env_var: str, default: bool = False) -> bool:
78*523fa7a6SAndroid Build Coastguard Worker        val = os.environ.get(env_var, None)
79*523fa7a6SAndroid Build Coastguard Worker        if val is None:
80*523fa7a6SAndroid Build Coastguard Worker            return default
81*523fa7a6SAndroid Build Coastguard Worker        if val in ("OFF", "0", ""):
82*523fa7a6SAndroid Build Coastguard Worker            return False
83*523fa7a6SAndroid Build Coastguard Worker        return True
84*523fa7a6SAndroid Build Coastguard Worker
85*523fa7a6SAndroid Build Coastguard Worker    @classmethod
86*523fa7a6SAndroid Build Coastguard Worker    def pybindings(cls) -> bool:
87*523fa7a6SAndroid Build Coastguard Worker        return cls._is_env_enabled("EXECUTORCH_BUILD_PYBIND", default=False)
88*523fa7a6SAndroid Build Coastguard Worker
89*523fa7a6SAndroid Build Coastguard Worker    @classmethod
90*523fa7a6SAndroid Build Coastguard Worker    def llama_custom_ops(cls) -> bool:
91*523fa7a6SAndroid Build Coastguard Worker        return cls._is_env_enabled("EXECUTORCH_BUILD_KERNELS_CUSTOM_AOT", default=True)
92*523fa7a6SAndroid Build Coastguard Worker
93*523fa7a6SAndroid Build Coastguard Worker    @classmethod
94*523fa7a6SAndroid Build Coastguard Worker    def flatc(cls) -> bool:
95*523fa7a6SAndroid Build Coastguard Worker        return cls._is_env_enabled("EXECUTORCH_BUILD_FLATC", default=True)
96*523fa7a6SAndroid Build Coastguard Worker
97*523fa7a6SAndroid Build Coastguard Worker
98*523fa7a6SAndroid Build Coastguard Workerclass Version:
99*523fa7a6SAndroid Build Coastguard Worker    """Static strings that describe the version of the pip package."""
100*523fa7a6SAndroid Build Coastguard Worker
101*523fa7a6SAndroid Build Coastguard Worker    # Cached values returned by the properties.
102*523fa7a6SAndroid Build Coastguard Worker    __root_dir_attr: Optional[str] = None
103*523fa7a6SAndroid Build Coastguard Worker    __string_attr: Optional[str] = None
104*523fa7a6SAndroid Build Coastguard Worker    __git_hash_attr: Optional[str] = None
105*523fa7a6SAndroid Build Coastguard Worker
106*523fa7a6SAndroid Build Coastguard Worker    @classmethod
107*523fa7a6SAndroid Build Coastguard Worker    def _root_dir(cls) -> str:
108*523fa7a6SAndroid Build Coastguard Worker        """The path to the root of the git repo."""
109*523fa7a6SAndroid Build Coastguard Worker        if cls.__root_dir_attr is None:
110*523fa7a6SAndroid Build Coastguard Worker            # This setup.py file lives in the root of the repo.
111*523fa7a6SAndroid Build Coastguard Worker            cls.__root_dir_attr = str(Path(__file__).parent.resolve())
112*523fa7a6SAndroid Build Coastguard Worker        return str(cls.__root_dir_attr)
113*523fa7a6SAndroid Build Coastguard Worker
114*523fa7a6SAndroid Build Coastguard Worker    @classmethod
115*523fa7a6SAndroid Build Coastguard Worker    def git_hash(cls) -> Optional[str]:
116*523fa7a6SAndroid Build Coastguard Worker        """The current git hash, if known."""
117*523fa7a6SAndroid Build Coastguard Worker        if cls.__git_hash_attr is None:
118*523fa7a6SAndroid Build Coastguard Worker            import subprocess
119*523fa7a6SAndroid Build Coastguard Worker
120*523fa7a6SAndroid Build Coastguard Worker            try:
121*523fa7a6SAndroid Build Coastguard Worker                cls.__git_hash_attr = (
122*523fa7a6SAndroid Build Coastguard Worker                    subprocess.check_output(
123*523fa7a6SAndroid Build Coastguard Worker                        ["git", "rev-parse", "HEAD"], cwd=cls._root_dir()
124*523fa7a6SAndroid Build Coastguard Worker                    )
125*523fa7a6SAndroid Build Coastguard Worker                    .decode("ascii")
126*523fa7a6SAndroid Build Coastguard Worker                    .strip()
127*523fa7a6SAndroid Build Coastguard Worker                )
128*523fa7a6SAndroid Build Coastguard Worker            except subprocess.CalledProcessError:
129*523fa7a6SAndroid Build Coastguard Worker                cls.__git_hash_attr = ""  # Non-None but empty.
130*523fa7a6SAndroid Build Coastguard Worker        # A non-None but empty value indicates that we don't know it.
131*523fa7a6SAndroid Build Coastguard Worker        return cls.__git_hash_attr if cls.__git_hash_attr else None
132*523fa7a6SAndroid Build Coastguard Worker
133*523fa7a6SAndroid Build Coastguard Worker    @classmethod
134*523fa7a6SAndroid Build Coastguard Worker    def string(cls) -> str:
135*523fa7a6SAndroid Build Coastguard Worker        """The version string."""
136*523fa7a6SAndroid Build Coastguard Worker        if cls.__string_attr is None:
137*523fa7a6SAndroid Build Coastguard Worker            # If set, BUILD_VERSION should override any local version
138*523fa7a6SAndroid Build Coastguard Worker            # information. CI will use this to manage, e.g., release vs. nightly
139*523fa7a6SAndroid Build Coastguard Worker            # versions.
140*523fa7a6SAndroid Build Coastguard Worker            version = os.getenv("BUILD_VERSION", "").strip()
141*523fa7a6SAndroid Build Coastguard Worker            if not version:
142*523fa7a6SAndroid Build Coastguard Worker                # Otherwise, read the version from a local file and add the git
143*523fa7a6SAndroid Build Coastguard Worker                # commit if available.
144*523fa7a6SAndroid Build Coastguard Worker                version = (
145*523fa7a6SAndroid Build Coastguard Worker                    open(os.path.join(cls._root_dir(), "version.txt")).read().strip()
146*523fa7a6SAndroid Build Coastguard Worker                )
147*523fa7a6SAndroid Build Coastguard Worker                if cls.git_hash():
148*523fa7a6SAndroid Build Coastguard Worker                    version += "+" + cls.git_hash()[:7]
149*523fa7a6SAndroid Build Coastguard Worker            cls.__string_attr = version
150*523fa7a6SAndroid Build Coastguard Worker        return cls.__string_attr
151*523fa7a6SAndroid Build Coastguard Worker
152*523fa7a6SAndroid Build Coastguard Worker    @classmethod
153*523fa7a6SAndroid Build Coastguard Worker    def write_to_python_file(cls, path: str) -> None:
154*523fa7a6SAndroid Build Coastguard Worker        """Creates a file similar to PyTorch core's `torch/version.py`."""
155*523fa7a6SAndroid Build Coastguard Worker        lines = [
156*523fa7a6SAndroid Build Coastguard Worker            "from typing import Optional",
157*523fa7a6SAndroid Build Coastguard Worker            '__all__ = ["__version__", "git_version"]',
158*523fa7a6SAndroid Build Coastguard Worker            f'__version__ = "{cls.string()}"',
159*523fa7a6SAndroid Build Coastguard Worker            # A string or None.
160*523fa7a6SAndroid Build Coastguard Worker            f"git_version: Optional[str] = {repr(cls.git_hash())}",
161*523fa7a6SAndroid Build Coastguard Worker        ]
162*523fa7a6SAndroid Build Coastguard Worker        with open(path, "w") as fp:
163*523fa7a6SAndroid Build Coastguard Worker            fp.write("\n".join(lines) + "\n")
164*523fa7a6SAndroid Build Coastguard Worker
165*523fa7a6SAndroid Build Coastguard Worker
166*523fa7a6SAndroid Build Coastguard Worker# The build type is determined by the DEBUG environment variable. If DEBUG is
167*523fa7a6SAndroid Build Coastguard Worker# set to a non-empty value, the build type is Debug. Otherwise, the build type
168*523fa7a6SAndroid Build Coastguard Worker# is Release.
169*523fa7a6SAndroid Build Coastguard Workerdef get_build_type(is_debug=None) -> str:
170*523fa7a6SAndroid Build Coastguard Worker    debug = int(os.environ.get("DEBUG", 0)) if is_debug is None else is_debug
171*523fa7a6SAndroid Build Coastguard Worker    cfg = "Debug" if debug else "Release"
172*523fa7a6SAndroid Build Coastguard Worker    return cfg
173*523fa7a6SAndroid Build Coastguard Worker
174*523fa7a6SAndroid Build Coastguard Worker
175*523fa7a6SAndroid Build Coastguard Workerdef get_dynamic_lib_name(name: str) -> str:
176*523fa7a6SAndroid Build Coastguard Worker    if platform.system() == "Windows":
177*523fa7a6SAndroid Build Coastguard Worker        return name + ".dll"
178*523fa7a6SAndroid Build Coastguard Worker    elif platform.system() == "Darwin":
179*523fa7a6SAndroid Build Coastguard Worker        return "lib" + name + ".dylib"
180*523fa7a6SAndroid Build Coastguard Worker    else:
181*523fa7a6SAndroid Build Coastguard Worker        return "lib" + name + ".so"
182*523fa7a6SAndroid Build Coastguard Worker
183*523fa7a6SAndroid Build Coastguard Worker
184*523fa7a6SAndroid Build Coastguard Workerdef get_executable_name(name: str) -> str:
185*523fa7a6SAndroid Build Coastguard Worker    if platform.system() == "Windows":
186*523fa7a6SAndroid Build Coastguard Worker        return name + ".exe"
187*523fa7a6SAndroid Build Coastguard Worker    else:
188*523fa7a6SAndroid Build Coastguard Worker        return name
189*523fa7a6SAndroid Build Coastguard Worker
190*523fa7a6SAndroid Build Coastguard Worker
191*523fa7a6SAndroid Build Coastguard Workerclass _BaseExtension(Extension):
192*523fa7a6SAndroid Build Coastguard Worker    """A base class that maps an abstract source to an abstract destination."""
193*523fa7a6SAndroid Build Coastguard Worker
194*523fa7a6SAndroid Build Coastguard Worker    def __init__(self, src: str, dst: str, name: str):
195*523fa7a6SAndroid Build Coastguard Worker        # Source path; semantics defined by the subclass.
196*523fa7a6SAndroid Build Coastguard Worker        self.src: str = src
197*523fa7a6SAndroid Build Coastguard Worker
198*523fa7a6SAndroid Build Coastguard Worker        # Destination path relative to a namespace defined elsewhere. If this ends
199*523fa7a6SAndroid Build Coastguard Worker        # in "/", it is treated as a directory. If this is "", it is treated as the
200*523fa7a6SAndroid Build Coastguard Worker        # root of the namespace.
201*523fa7a6SAndroid Build Coastguard Worker        # Destination path; semantics defined by the subclass.
202*523fa7a6SAndroid Build Coastguard Worker        self.dst: str = dst
203*523fa7a6SAndroid Build Coastguard Worker
204*523fa7a6SAndroid Build Coastguard Worker        # Other parts of setuptools expects .name to exist. For actual extensions
205*523fa7a6SAndroid Build Coastguard Worker        # this can be the module path, but otherwise it should be somehing unique
206*523fa7a6SAndroid Build Coastguard Worker        # that doesn't look like a module path.
207*523fa7a6SAndroid Build Coastguard Worker        self.name: str = name
208*523fa7a6SAndroid Build Coastguard Worker
209*523fa7a6SAndroid Build Coastguard Worker        super().__init__(name=self.name, sources=[])
210*523fa7a6SAndroid Build Coastguard Worker
211*523fa7a6SAndroid Build Coastguard Worker    def src_path(self, installer: "InstallerBuildExt") -> Path:
212*523fa7a6SAndroid Build Coastguard Worker        """Returns the path to the source file, resolving globs.
213*523fa7a6SAndroid Build Coastguard Worker
214*523fa7a6SAndroid Build Coastguard Worker        Args:
215*523fa7a6SAndroid Build Coastguard Worker            installer: The InstallerBuildExt instance that is installing the
216*523fa7a6SAndroid Build Coastguard Worker                file.
217*523fa7a6SAndroid Build Coastguard Worker        """
218*523fa7a6SAndroid Build Coastguard Worker        # Share the cmake-out location with CustomBuild.
219*523fa7a6SAndroid Build Coastguard Worker        cmake_cache_dir = Path(installer.get_finalized_command("build").cmake_cache_dir)
220*523fa7a6SAndroid Build Coastguard Worker
221*523fa7a6SAndroid Build Coastguard Worker        cfg = get_build_type(installer.debug)
222*523fa7a6SAndroid Build Coastguard Worker
223*523fa7a6SAndroid Build Coastguard Worker        if os.name == "nt":
224*523fa7a6SAndroid Build Coastguard Worker            # Replace %BUILD_TYPE% with the current build type.
225*523fa7a6SAndroid Build Coastguard Worker            self.src = self.src.replace("%BUILD_TYPE%", cfg)
226*523fa7a6SAndroid Build Coastguard Worker        else:
227*523fa7a6SAndroid Build Coastguard Worker            # Remove %BUILD_TYPE% from the path.
228*523fa7a6SAndroid Build Coastguard Worker            self.src = self.src.replace("/%BUILD_TYPE%", "")
229*523fa7a6SAndroid Build Coastguard Worker
230*523fa7a6SAndroid Build Coastguard Worker        # Construct the full source path, resolving globs. If there are no glob
231*523fa7a6SAndroid Build Coastguard Worker        # pattern characters, this will just ensure that the source file exists.
232*523fa7a6SAndroid Build Coastguard Worker        srcs = tuple(cmake_cache_dir.glob(self.src))
233*523fa7a6SAndroid Build Coastguard Worker        if len(srcs) != 1:
234*523fa7a6SAndroid Build Coastguard Worker            raise ValueError(
235*523fa7a6SAndroid Build Coastguard Worker                f"Expected exactly one file matching '{self.src}'; found {repr(srcs)}"
236*523fa7a6SAndroid Build Coastguard Worker            )
237*523fa7a6SAndroid Build Coastguard Worker        return srcs[0]
238*523fa7a6SAndroid Build Coastguard Worker
239*523fa7a6SAndroid Build Coastguard Worker
240*523fa7a6SAndroid Build Coastguard Workerclass BuiltFile(_BaseExtension):
241*523fa7a6SAndroid Build Coastguard Worker    """An extension that installs a single file that was built by cmake.
242*523fa7a6SAndroid Build Coastguard Worker
243*523fa7a6SAndroid Build Coastguard Worker    This isn't technically a `build_ext` style python extension, but there's no
244*523fa7a6SAndroid Build Coastguard Worker    dedicated command for installing arbitrary data. It's convenient to use
245*523fa7a6SAndroid Build Coastguard Worker    this, though, because it lets us manage the files to install as entries in
246*523fa7a6SAndroid Build Coastguard Worker    `ext_modules`.
247*523fa7a6SAndroid Build Coastguard Worker    """
248*523fa7a6SAndroid Build Coastguard Worker
249*523fa7a6SAndroid Build Coastguard Worker    def __init__(
250*523fa7a6SAndroid Build Coastguard Worker        self,
251*523fa7a6SAndroid Build Coastguard Worker        src_dir: str,
252*523fa7a6SAndroid Build Coastguard Worker        src_name: str,
253*523fa7a6SAndroid Build Coastguard Worker        dst: str,
254*523fa7a6SAndroid Build Coastguard Worker        is_executable: bool = False,
255*523fa7a6SAndroid Build Coastguard Worker        is_dynamic_lib: bool = False,
256*523fa7a6SAndroid Build Coastguard Worker    ):
257*523fa7a6SAndroid Build Coastguard Worker        """Initializes a BuiltFile.
258*523fa7a6SAndroid Build Coastguard Worker
259*523fa7a6SAndroid Build Coastguard Worker        Args:
260*523fa7a6SAndroid Build Coastguard Worker            src_dir: The directory of the file to install, relative to the cmake-out
261*523fa7a6SAndroid Build Coastguard Worker                directory. A placeholder %BUILD_TYPE% will be replaced with the build
262*523fa7a6SAndroid Build Coastguard Worker                type for multi-config generators (like Visual Studio) where the build
263*523fa7a6SAndroid Build Coastguard Worker                output is in a subdirectory named after the build type. For single-
264*523fa7a6SAndroid Build Coastguard Worker                config generators (like Makefile Generators or Ninja), this placeholder
265*523fa7a6SAndroid Build Coastguard Worker                will be removed.
266*523fa7a6SAndroid Build Coastguard Worker            src_name: The name of the file to install
267*523fa7a6SAndroid Build Coastguard Worker            dst: The path to install to, relative to the root of the pip
268*523fa7a6SAndroid Build Coastguard Worker                package. If dst ends in "/", it is treated as a directory.
269*523fa7a6SAndroid Build Coastguard Worker                Otherwise it is treated as a filename.
270*523fa7a6SAndroid Build Coastguard Worker            is_executable: If True, the file is an executable. This is used to
271*523fa7a6SAndroid Build Coastguard Worker                determine the destination filename for executable.
272*523fa7a6SAndroid Build Coastguard Worker            is_dynamic_lib: If True, the file is a dynamic library. This is used
273*523fa7a6SAndroid Build Coastguard Worker                to determine the destination filename for dynamic library.
274*523fa7a6SAndroid Build Coastguard Worker        """
275*523fa7a6SAndroid Build Coastguard Worker        if is_executable and is_dynamic_lib:
276*523fa7a6SAndroid Build Coastguard Worker            raise ValueError("is_executable and is_dynamic_lib cannot be both True.")
277*523fa7a6SAndroid Build Coastguard Worker        if is_executable:
278*523fa7a6SAndroid Build Coastguard Worker            src_name = get_executable_name(src_name)
279*523fa7a6SAndroid Build Coastguard Worker        elif is_dynamic_lib:
280*523fa7a6SAndroid Build Coastguard Worker            src_name = get_dynamic_lib_name(src_name)
281*523fa7a6SAndroid Build Coastguard Worker        src = os.path.join(src_dir, src_name)
282*523fa7a6SAndroid Build Coastguard Worker        # This is not a real extension, so use a unique name that doesn't look
283*523fa7a6SAndroid Build Coastguard Worker        # like a module path. Some of setuptools's autodiscovery will look for
284*523fa7a6SAndroid Build Coastguard Worker        # extension names with prefixes that match certain module paths.
285*523fa7a6SAndroid Build Coastguard Worker        super().__init__(src=src, dst=dst, name=f"@EXECUTORCH_BuiltFile_{src}:{dst}")
286*523fa7a6SAndroid Build Coastguard Worker
287*523fa7a6SAndroid Build Coastguard Worker    def dst_path(self, installer: "InstallerBuildExt") -> Path:
288*523fa7a6SAndroid Build Coastguard Worker        """Returns the path to the destination file.
289*523fa7a6SAndroid Build Coastguard Worker
290*523fa7a6SAndroid Build Coastguard Worker        Args:
291*523fa7a6SAndroid Build Coastguard Worker            installer: The InstallerBuildExt instance that is installing the
292*523fa7a6SAndroid Build Coastguard Worker                file.
293*523fa7a6SAndroid Build Coastguard Worker        """
294*523fa7a6SAndroid Build Coastguard Worker        dst_root = Path(installer.build_lib).resolve()
295*523fa7a6SAndroid Build Coastguard Worker
296*523fa7a6SAndroid Build Coastguard Worker        if self.dst.endswith("/"):
297*523fa7a6SAndroid Build Coastguard Worker            # Destination looks like a directory. Use the basename of the source
298*523fa7a6SAndroid Build Coastguard Worker            # file for its final component.
299*523fa7a6SAndroid Build Coastguard Worker            return dst_root / Path(self.dst) / self.src_path(installer).name
300*523fa7a6SAndroid Build Coastguard Worker        else:
301*523fa7a6SAndroid Build Coastguard Worker            # Destination looks like a file.
302*523fa7a6SAndroid Build Coastguard Worker            return dst_root / Path(self.dst)
303*523fa7a6SAndroid Build Coastguard Worker
304*523fa7a6SAndroid Build Coastguard Worker
305*523fa7a6SAndroid Build Coastguard Workerclass BuiltExtension(_BaseExtension):
306*523fa7a6SAndroid Build Coastguard Worker    """An extension that installs a python extension that was built by cmake."""
307*523fa7a6SAndroid Build Coastguard Worker
308*523fa7a6SAndroid Build Coastguard Worker    def __init__(self, src: str, modpath: str):
309*523fa7a6SAndroid Build Coastguard Worker        """Initializes a BuiltExtension.
310*523fa7a6SAndroid Build Coastguard Worker
311*523fa7a6SAndroid Build Coastguard Worker        Args:
312*523fa7a6SAndroid Build Coastguard Worker            src: The path to the file to install (typically a shared library),
313*523fa7a6SAndroid Build Coastguard Worker                relative to the cmake-out directory. May be an fnmatch-style
314*523fa7a6SAndroid Build Coastguard Worker                glob that matches exactly one file. If the path ends in `.so`,
315*523fa7a6SAndroid Build Coastguard Worker                this class will also look for similarly-named `.dylib` files.
316*523fa7a6SAndroid Build Coastguard Worker            modpath: The dotted path of the python module that maps to the
317*523fa7a6SAndroid Build Coastguard Worker                extension.
318*523fa7a6SAndroid Build Coastguard Worker        """
319*523fa7a6SAndroid Build Coastguard Worker        assert (
320*523fa7a6SAndroid Build Coastguard Worker            "/" not in modpath
321*523fa7a6SAndroid Build Coastguard Worker        ), f"modpath must be a dotted python module path: saw '{modpath}'"
322*523fa7a6SAndroid Build Coastguard Worker        # This is a real extension, so use the modpath as the name.
323*523fa7a6SAndroid Build Coastguard Worker        super().__init__(src=src, dst=modpath, name=modpath)
324*523fa7a6SAndroid Build Coastguard Worker
325*523fa7a6SAndroid Build Coastguard Worker    def src_path(self, installer: "InstallerBuildExt") -> Path:
326*523fa7a6SAndroid Build Coastguard Worker        """Returns the path to the source file, resolving globs.
327*523fa7a6SAndroid Build Coastguard Worker
328*523fa7a6SAndroid Build Coastguard Worker        Args:
329*523fa7a6SAndroid Build Coastguard Worker            installer: The InstallerBuildExt instance that is installing the
330*523fa7a6SAndroid Build Coastguard Worker                file.
331*523fa7a6SAndroid Build Coastguard Worker        """
332*523fa7a6SAndroid Build Coastguard Worker        try:
333*523fa7a6SAndroid Build Coastguard Worker            return super().src_path(installer)
334*523fa7a6SAndroid Build Coastguard Worker        except ValueError:
335*523fa7a6SAndroid Build Coastguard Worker            # Probably couldn't find the file. If the path ends with .so, try
336*523fa7a6SAndroid Build Coastguard Worker            # looking for a .dylib file instead, in case we're running on macos.
337*523fa7a6SAndroid Build Coastguard Worker            if self.src.endswith(".so"):
338*523fa7a6SAndroid Build Coastguard Worker                dylib_src = re.sub(r"\.so$", ".dylib", self.src)
339*523fa7a6SAndroid Build Coastguard Worker                return BuiltExtension(src=dylib_src, modpath=self.dst).src_path(
340*523fa7a6SAndroid Build Coastguard Worker                    installer
341*523fa7a6SAndroid Build Coastguard Worker                )
342*523fa7a6SAndroid Build Coastguard Worker            else:
343*523fa7a6SAndroid Build Coastguard Worker                raise
344*523fa7a6SAndroid Build Coastguard Worker
345*523fa7a6SAndroid Build Coastguard Worker    def dst_path(self, installer: "InstallerBuildExt") -> Path:
346*523fa7a6SAndroid Build Coastguard Worker        """Returns the path to the destination file.
347*523fa7a6SAndroid Build Coastguard Worker
348*523fa7a6SAndroid Build Coastguard Worker        Args:
349*523fa7a6SAndroid Build Coastguard Worker            installer: The InstallerBuildExt instance that is installing the
350*523fa7a6SAndroid Build Coastguard Worker                file.
351*523fa7a6SAndroid Build Coastguard Worker        """
352*523fa7a6SAndroid Build Coastguard Worker        # Our destination is a dotted module path. get_ext_fullpath() returns
353*523fa7a6SAndroid Build Coastguard Worker        # the relative path to the .so/.dylib/etc. file that maps to the module
354*523fa7a6SAndroid Build Coastguard Worker        # path: that's the file we're creating.
355*523fa7a6SAndroid Build Coastguard Worker        return Path(installer.get_ext_fullpath(self.dst))
356*523fa7a6SAndroid Build Coastguard Worker
357*523fa7a6SAndroid Build Coastguard Worker
358*523fa7a6SAndroid Build Coastguard Workerclass InstallerBuildExt(build_ext):
359*523fa7a6SAndroid Build Coastguard Worker    """Installs files that were built by cmake."""
360*523fa7a6SAndroid Build Coastguard Worker
361*523fa7a6SAndroid Build Coastguard Worker    # TODO(dbort): Depend on the "build" command to ensure it runs first
362*523fa7a6SAndroid Build Coastguard Worker
363*523fa7a6SAndroid Build Coastguard Worker    def build_extension(self, ext: _BaseExtension) -> None:
364*523fa7a6SAndroid Build Coastguard Worker        src_file: Path = ext.src_path(self)
365*523fa7a6SAndroid Build Coastguard Worker        dst_file: Path = ext.dst_path(self)
366*523fa7a6SAndroid Build Coastguard Worker
367*523fa7a6SAndroid Build Coastguard Worker        # Ensure that the destination directory exists.
368*523fa7a6SAndroid Build Coastguard Worker        self.mkpath(os.fspath(dst_file.parent))
369*523fa7a6SAndroid Build Coastguard Worker
370*523fa7a6SAndroid Build Coastguard Worker        # Copy the file.
371*523fa7a6SAndroid Build Coastguard Worker        self.copy_file(os.fspath(src_file), os.fspath(dst_file))
372*523fa7a6SAndroid Build Coastguard Worker
373*523fa7a6SAndroid Build Coastguard Worker        # Ensure that the destination file is writable, even if the source was
374*523fa7a6SAndroid Build Coastguard Worker        # not. build_py does this by passing preserve_mode=False to copy_file,
375*523fa7a6SAndroid Build Coastguard Worker        # but that would clobber the X bit on any executables. TODO(dbort): This
376*523fa7a6SAndroid Build Coastguard Worker        # probably won't work on Windows.
377*523fa7a6SAndroid Build Coastguard Worker        if not os.access(src_file, os.W_OK):
378*523fa7a6SAndroid Build Coastguard Worker            # Make the file writable. This should respect the umask.
379*523fa7a6SAndroid Build Coastguard Worker            os.chmod(src_file, os.stat(src_file).st_mode | 0o222)
380*523fa7a6SAndroid Build Coastguard Worker
381*523fa7a6SAndroid Build Coastguard Worker
382*523fa7a6SAndroid Build Coastguard Workerclass CustomBuildPy(build_py):
383*523fa7a6SAndroid Build Coastguard Worker    """Copies platform-independent files from the source tree into the output
384*523fa7a6SAndroid Build Coastguard Worker    package directory.
385*523fa7a6SAndroid Build Coastguard Worker
386*523fa7a6SAndroid Build Coastguard Worker    Override it so we can copy some files to locations that don't match their
387*523fa7a6SAndroid Build Coastguard Worker    original relative locations.
388*523fa7a6SAndroid Build Coastguard Worker
389*523fa7a6SAndroid Build Coastguard Worker    Standard setuptools features like package_data and MANIFEST.in can only
390*523fa7a6SAndroid Build Coastguard Worker    include or exclude a file in the source tree; they don't have a way to map
391*523fa7a6SAndroid Build Coastguard Worker    a file to a different relative location under the output package directory.
392*523fa7a6SAndroid Build Coastguard Worker    """
393*523fa7a6SAndroid Build Coastguard Worker
394*523fa7a6SAndroid Build Coastguard Worker    def run(self):
395*523fa7a6SAndroid Build Coastguard Worker        # Copy python files to the output directory. This set of files is
396*523fa7a6SAndroid Build Coastguard Worker        # defined by the py_module list and package_data patterns.
397*523fa7a6SAndroid Build Coastguard Worker        build_py.run(self)
398*523fa7a6SAndroid Build Coastguard Worker
399*523fa7a6SAndroid Build Coastguard Worker        # dst_root is the root of the `executorch` module in the output package
400*523fa7a6SAndroid Build Coastguard Worker        # directory. build_lib is the platform-independent root of the output
401*523fa7a6SAndroid Build Coastguard Worker        # package, and will look like `pip-out/lib`. It can contain multiple
402*523fa7a6SAndroid Build Coastguard Worker        # python packages, so be sure to copy the files into the `executorch`
403*523fa7a6SAndroid Build Coastguard Worker        # package subdirectory.
404*523fa7a6SAndroid Build Coastguard Worker        dst_root = os.path.join(self.build_lib, self.get_package_dir("executorch"))
405*523fa7a6SAndroid Build Coastguard Worker
406*523fa7a6SAndroid Build Coastguard Worker        # Create the version file.
407*523fa7a6SAndroid Build Coastguard Worker        Version.write_to_python_file(os.path.join(dst_root, "version.py"))
408*523fa7a6SAndroid Build Coastguard Worker
409*523fa7a6SAndroid Build Coastguard Worker        # Manually copy files into the output package directory. These are
410*523fa7a6SAndroid Build Coastguard Worker        # typically python "resource" files that will live alongside the python
411*523fa7a6SAndroid Build Coastguard Worker        # code that uses them.
412*523fa7a6SAndroid Build Coastguard Worker        src_to_dst = [
413*523fa7a6SAndroid Build Coastguard Worker            # TODO(dbort): See if we can add a custom pyproject.toml section for
414*523fa7a6SAndroid Build Coastguard Worker            # these, instead of hard-coding them here. See
415*523fa7a6SAndroid Build Coastguard Worker            # https://setuptools.pypa.io/en/latest/userguide/extension.html
416*523fa7a6SAndroid Build Coastguard Worker            ("schema/scalar_type.fbs", "exir/_serialize/scalar_type.fbs"),
417*523fa7a6SAndroid Build Coastguard Worker            ("schema/program.fbs", "exir/_serialize/program.fbs"),
418*523fa7a6SAndroid Build Coastguard Worker            (
419*523fa7a6SAndroid Build Coastguard Worker                "devtools/bundled_program/schema/bundled_program_schema.fbs",
420*523fa7a6SAndroid Build Coastguard Worker                "devtools/bundled_program/serialize/bundled_program_schema.fbs",
421*523fa7a6SAndroid Build Coastguard Worker            ),
422*523fa7a6SAndroid Build Coastguard Worker            (
423*523fa7a6SAndroid Build Coastguard Worker                "devtools/bundled_program/schema/scalar_type.fbs",
424*523fa7a6SAndroid Build Coastguard Worker                "devtools/bundled_program/serialize/scalar_type.fbs",
425*523fa7a6SAndroid Build Coastguard Worker            ),
426*523fa7a6SAndroid Build Coastguard Worker            # Install executorch-wheel-config.cmake to pip package.
427*523fa7a6SAndroid Build Coastguard Worker            (
428*523fa7a6SAndroid Build Coastguard Worker                "build/executorch-wheel-config.cmake",
429*523fa7a6SAndroid Build Coastguard Worker                "share/cmake/executorch-config.cmake",
430*523fa7a6SAndroid Build Coastguard Worker            ),
431*523fa7a6SAndroid Build Coastguard Worker        ]
432*523fa7a6SAndroid Build Coastguard Worker        # Copy all the necessary headers into include/executorch/ so that they can
433*523fa7a6SAndroid Build Coastguard Worker        # be found in the pip package. This is the subset of headers that are
434*523fa7a6SAndroid Build Coastguard Worker        # essential for building custom ops extensions.
435*523fa7a6SAndroid Build Coastguard Worker        # TODO: Use cmake to gather the headers instead of hard-coding them here.
436*523fa7a6SAndroid Build Coastguard Worker        # For example: https://discourse.cmake.org/t/installing-headers-the-modern-
437*523fa7a6SAndroid Build Coastguard Worker        # way-regurgitated-and-revisited/3238/3
438*523fa7a6SAndroid Build Coastguard Worker        for include_dir in [
439*523fa7a6SAndroid Build Coastguard Worker            "runtime/core/",
440*523fa7a6SAndroid Build Coastguard Worker            "runtime/kernel/",
441*523fa7a6SAndroid Build Coastguard Worker            "runtime/platform/",
442*523fa7a6SAndroid Build Coastguard Worker            "extension/kernel_util/",
443*523fa7a6SAndroid Build Coastguard Worker            "extension/tensor/",
444*523fa7a6SAndroid Build Coastguard Worker            "extension/threadpool/",
445*523fa7a6SAndroid Build Coastguard Worker        ]:
446*523fa7a6SAndroid Build Coastguard Worker            src_list = Path(include_dir).rglob("*.h")
447*523fa7a6SAndroid Build Coastguard Worker            for src in src_list:
448*523fa7a6SAndroid Build Coastguard Worker                src_to_dst.append(
449*523fa7a6SAndroid Build Coastguard Worker                    (str(src), os.path.join("include/executorch", str(src)))
450*523fa7a6SAndroid Build Coastguard Worker                )
451*523fa7a6SAndroid Build Coastguard Worker        for src, dst in src_to_dst:
452*523fa7a6SAndroid Build Coastguard Worker            dst = os.path.join(dst_root, dst)
453*523fa7a6SAndroid Build Coastguard Worker
454*523fa7a6SAndroid Build Coastguard Worker            # When modifying the filesystem, use the self.* methods defined by
455*523fa7a6SAndroid Build Coastguard Worker            # Command to benefit from the same logging and dry_run logic as
456*523fa7a6SAndroid Build Coastguard Worker            # setuptools.
457*523fa7a6SAndroid Build Coastguard Worker
458*523fa7a6SAndroid Build Coastguard Worker            # Ensure that the destination directory exists.
459*523fa7a6SAndroid Build Coastguard Worker            self.mkpath(os.path.dirname(dst))
460*523fa7a6SAndroid Build Coastguard Worker            # Follow the example of the base build_py class by not preserving
461*523fa7a6SAndroid Build Coastguard Worker            # the mode. This ensures that the output file is read/write even if
462*523fa7a6SAndroid Build Coastguard Worker            # the input file is read-only.
463*523fa7a6SAndroid Build Coastguard Worker            self.copy_file(src, dst, preserve_mode=False)
464*523fa7a6SAndroid Build Coastguard Worker
465*523fa7a6SAndroid Build Coastguard Worker
466*523fa7a6SAndroid Build Coastguard Workerclass Buck2EnvironmentFixer(contextlib.AbstractContextManager):
467*523fa7a6SAndroid Build Coastguard Worker    """Removes HOME from the environment when running as root.
468*523fa7a6SAndroid Build Coastguard Worker
469*523fa7a6SAndroid Build Coastguard Worker    This script is sometimes run as root in docker containers. buck2 doesn't
470*523fa7a6SAndroid Build Coastguard Worker    allow running as root unless $HOME is owned by root or is not set.
471*523fa7a6SAndroid Build Coastguard Worker
472*523fa7a6SAndroid Build Coastguard Worker    TODO(pytorch/test-infra#5091): Remove this once the CI jobs stop running as
473*523fa7a6SAndroid Build Coastguard Worker    root.
474*523fa7a6SAndroid Build Coastguard Worker    """
475*523fa7a6SAndroid Build Coastguard Worker
476*523fa7a6SAndroid Build Coastguard Worker    def __init__(self):
477*523fa7a6SAndroid Build Coastguard Worker        self.saved_env = {}
478*523fa7a6SAndroid Build Coastguard Worker
479*523fa7a6SAndroid Build Coastguard Worker    def __enter__(self):
480*523fa7a6SAndroid Build Coastguard Worker        if os.name != "nt" and os.geteuid() == 0 and "HOME" in os.environ:
481*523fa7a6SAndroid Build Coastguard Worker            log.info("temporarily unsetting HOME while running as root")
482*523fa7a6SAndroid Build Coastguard Worker            self.saved_env["HOME"] = os.environ.pop("HOME")
483*523fa7a6SAndroid Build Coastguard Worker        return self
484*523fa7a6SAndroid Build Coastguard Worker
485*523fa7a6SAndroid Build Coastguard Worker    def __exit__(self, *args, **kwargs):
486*523fa7a6SAndroid Build Coastguard Worker        if "HOME" in self.saved_env:
487*523fa7a6SAndroid Build Coastguard Worker            log.info("restored HOME")
488*523fa7a6SAndroid Build Coastguard Worker            os.environ["HOME"] = self.saved_env["HOME"]
489*523fa7a6SAndroid Build Coastguard Worker
490*523fa7a6SAndroid Build Coastguard Worker
491*523fa7a6SAndroid Build Coastguard Worker# TODO(dbort): For editable wheels, may need to update get_source_files(),
492*523fa7a6SAndroid Build Coastguard Worker# get_outputs(), and get_output_mapping() to satisfy
493*523fa7a6SAndroid Build Coastguard Worker# https://setuptools.pypa.io/en/latest/userguide/extension.html#setuptools.command.build.SubCommand.get_output_mapping
494*523fa7a6SAndroid Build Coastguard Worker
495*523fa7a6SAndroid Build Coastguard Worker
496*523fa7a6SAndroid Build Coastguard Workerclass CustomBuild(build):
497*523fa7a6SAndroid Build Coastguard Worker    def initialize_options(self):
498*523fa7a6SAndroid Build Coastguard Worker        super().initialize_options()
499*523fa7a6SAndroid Build Coastguard Worker        # The default build_base directory is called "build", but we have a
500*523fa7a6SAndroid Build Coastguard Worker        # top-level directory with that name. Setting build_base in setup()
501*523fa7a6SAndroid Build Coastguard Worker        # doesn't affect this, so override the core build command.
502*523fa7a6SAndroid Build Coastguard Worker        #
503*523fa7a6SAndroid Build Coastguard Worker        # See build.initialize_options() in
504*523fa7a6SAndroid Build Coastguard Worker        # setuptools/_distutils/command/build.py for the default.
505*523fa7a6SAndroid Build Coastguard Worker        self.build_base = "pip-out"
506*523fa7a6SAndroid Build Coastguard Worker
507*523fa7a6SAndroid Build Coastguard Worker        # Default build parallelism based on number of cores, but allow
508*523fa7a6SAndroid Build Coastguard Worker        # overriding through the environment.
509*523fa7a6SAndroid Build Coastguard Worker        default_parallel = str(os.cpu_count() - 1)
510*523fa7a6SAndroid Build Coastguard Worker        self.parallel = os.environ.get("CMAKE_BUILD_PARALLEL_LEVEL", default_parallel)
511*523fa7a6SAndroid Build Coastguard Worker
512*523fa7a6SAndroid Build Coastguard Worker    def run(self):
513*523fa7a6SAndroid Build Coastguard Worker        self.dump_options()
514*523fa7a6SAndroid Build Coastguard Worker
515*523fa7a6SAndroid Build Coastguard Worker        cfg = get_build_type(self.debug)
516*523fa7a6SAndroid Build Coastguard Worker
517*523fa7a6SAndroid Build Coastguard Worker        # get_python_lib() typically returns the path to site-packages, where
518*523fa7a6SAndroid Build Coastguard Worker        # all pip packages in the environment are installed.
519*523fa7a6SAndroid Build Coastguard Worker        cmake_prefix_path = os.environ.get("CMAKE_PREFIX_PATH", get_python_lib())
520*523fa7a6SAndroid Build Coastguard Worker
521*523fa7a6SAndroid Build Coastguard Worker        # The root of the repo should be the current working directory. Get
522*523fa7a6SAndroid Build Coastguard Worker        # the absolute path.
523*523fa7a6SAndroid Build Coastguard Worker        repo_root = os.fspath(Path.cwd())
524*523fa7a6SAndroid Build Coastguard Worker
525*523fa7a6SAndroid Build Coastguard Worker        # If blank, the cmake build system will find an appropriate binary.
526*523fa7a6SAndroid Build Coastguard Worker        buck2 = os.environ.get(
527*523fa7a6SAndroid Build Coastguard Worker            "BUCK2_EXECUTABLE", os.environ.get("BUCK2", os.environ.get("BUCK", ""))
528*523fa7a6SAndroid Build Coastguard Worker        )
529*523fa7a6SAndroid Build Coastguard Worker
530*523fa7a6SAndroid Build Coastguard Worker        cmake_args = [
531*523fa7a6SAndroid Build Coastguard Worker            f"-DBUCK2={buck2}",
532*523fa7a6SAndroid Build Coastguard Worker            f"-DPYTHON_EXECUTABLE={sys.executable}",
533*523fa7a6SAndroid Build Coastguard Worker            # Let cmake calls like `find_package(Torch)` find cmake config files
534*523fa7a6SAndroid Build Coastguard Worker            # like `TorchConfig.cmake` that are provided by pip packages.
535*523fa7a6SAndroid Build Coastguard Worker            f"-DCMAKE_PREFIX_PATH={cmake_prefix_path}",
536*523fa7a6SAndroid Build Coastguard Worker            f"-DCMAKE_BUILD_TYPE={cfg}",
537*523fa7a6SAndroid Build Coastguard Worker            # Enable logging even when in release mode. We are building for
538*523fa7a6SAndroid Build Coastguard Worker            # desktop, where saving a few kB is less important than showing
539*523fa7a6SAndroid Build Coastguard Worker            # useful error information to users.
540*523fa7a6SAndroid Build Coastguard Worker            "-DEXECUTORCH_ENABLE_LOGGING=ON",
541*523fa7a6SAndroid Build Coastguard Worker            "-DEXECUTORCH_LOG_LEVEL=Info",
542*523fa7a6SAndroid Build Coastguard Worker            "-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15",
543*523fa7a6SAndroid Build Coastguard Worker            # The separate host project is only required when cross-compiling,
544*523fa7a6SAndroid Build Coastguard Worker            # and it can cause build race conditions (libflatcc.a errors) when
545*523fa7a6SAndroid Build Coastguard Worker            # enabled. TODO(dbort): Remove this override once this option is
546*523fa7a6SAndroid Build Coastguard Worker            # managed by cmake itself.
547*523fa7a6SAndroid Build Coastguard Worker            "-DEXECUTORCH_SEPARATE_FLATCC_HOST_PROJECT=OFF",
548*523fa7a6SAndroid Build Coastguard Worker        ]
549*523fa7a6SAndroid Build Coastguard Worker
550*523fa7a6SAndroid Build Coastguard Worker        build_args = [f"-j{self.parallel}"]
551*523fa7a6SAndroid Build Coastguard Worker
552*523fa7a6SAndroid Build Coastguard Worker        # TODO(dbort): Try to manage these targets and the cmake args from the
553*523fa7a6SAndroid Build Coastguard Worker        # extension entries themselves instead of hard-coding them here.
554*523fa7a6SAndroid Build Coastguard Worker        build_args += ["--target", "flatc"]
555*523fa7a6SAndroid Build Coastguard Worker
556*523fa7a6SAndroid Build Coastguard Worker        if ShouldBuild.pybindings():
557*523fa7a6SAndroid Build Coastguard Worker            cmake_args += [
558*523fa7a6SAndroid Build Coastguard Worker                "-DEXECUTORCH_BUILD_PYBIND=ON",
559*523fa7a6SAndroid Build Coastguard Worker                "-DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON",  # add quantized ops to pybindings.
560*523fa7a6SAndroid Build Coastguard Worker                "-DEXECUTORCH_BUILD_KERNELS_QUANTIZED_AOT=ON",
561*523fa7a6SAndroid Build Coastguard Worker            ]
562*523fa7a6SAndroid Build Coastguard Worker            build_args += ["--target", "portable_lib"]
563*523fa7a6SAndroid Build Coastguard Worker            # To link backends into the portable_lib target, callers should
564*523fa7a6SAndroid Build Coastguard Worker            # add entries like `-DEXECUTORCH_BUILD_XNNPACK=ON` to the CMAKE_ARGS
565*523fa7a6SAndroid Build Coastguard Worker            # environment variable.
566*523fa7a6SAndroid Build Coastguard Worker
567*523fa7a6SAndroid Build Coastguard Worker        if ShouldBuild.llama_custom_ops():
568*523fa7a6SAndroid Build Coastguard Worker            cmake_args += [
569*523fa7a6SAndroid Build Coastguard Worker                "-DEXECUTORCH_BUILD_KERNELS_CUSTOM=ON",  # add llama sdpa ops to pybindings.
570*523fa7a6SAndroid Build Coastguard Worker                "-DEXECUTORCH_BUILD_KERNELS_CUSTOM_AOT=ON",
571*523fa7a6SAndroid Build Coastguard Worker                "-DEXECUTORCH_BUILD_KERNELS_QUANTIZED=ON",  # add quantized ops to pybindings.
572*523fa7a6SAndroid Build Coastguard Worker                "-DEXECUTORCH_BUILD_KERNELS_QUANTIZED_AOT=ON",
573*523fa7a6SAndroid Build Coastguard Worker            ]
574*523fa7a6SAndroid Build Coastguard Worker            build_args += ["--target", "custom_ops_aot_lib"]
575*523fa7a6SAndroid Build Coastguard Worker            build_args += ["--target", "quantized_ops_aot_lib"]
576*523fa7a6SAndroid Build Coastguard Worker        # Allow adding extra cmake args through the environment. Used by some
577*523fa7a6SAndroid Build Coastguard Worker        # tests and demos to expand the set of targets included in the pip
578*523fa7a6SAndroid Build Coastguard Worker        # package.
579*523fa7a6SAndroid Build Coastguard Worker        if "CMAKE_ARGS" in os.environ:
580*523fa7a6SAndroid Build Coastguard Worker            cmake_args += [item for item in os.environ["CMAKE_ARGS"].split(" ") if item]
581*523fa7a6SAndroid Build Coastguard Worker
582*523fa7a6SAndroid Build Coastguard Worker        # Allow adding extra build args through the environment. Used by some
583*523fa7a6SAndroid Build Coastguard Worker        # tests and demos to expand the set of targets included in the pip
584*523fa7a6SAndroid Build Coastguard Worker        # package.
585*523fa7a6SAndroid Build Coastguard Worker        if "CMAKE_BUILD_ARGS" in os.environ:
586*523fa7a6SAndroid Build Coastguard Worker            build_args += [
587*523fa7a6SAndroid Build Coastguard Worker                item for item in os.environ["CMAKE_BUILD_ARGS"].split(" ") if item
588*523fa7a6SAndroid Build Coastguard Worker            ]
589*523fa7a6SAndroid Build Coastguard Worker
590*523fa7a6SAndroid Build Coastguard Worker        # CMAKE_BUILD_TYPE variable specifies the build type (configuration) for
591*523fa7a6SAndroid Build Coastguard Worker        # single-configuration generators (e.g., Makefile Generators or Ninja).
592*523fa7a6SAndroid Build Coastguard Worker        # For multi-config generators (like Visual Studio), CMAKE_BUILD_TYPE
593*523fa7a6SAndroid Build Coastguard Worker        # isn’t directly applicable.
594*523fa7a6SAndroid Build Coastguard Worker        # During the build step, --config specifies the configuration to build
595*523fa7a6SAndroid Build Coastguard Worker        # for multi-config generators.
596*523fa7a6SAndroid Build Coastguard Worker        build_args += ["--config", cfg]
597*523fa7a6SAndroid Build Coastguard Worker
598*523fa7a6SAndroid Build Coastguard Worker        # Put the cmake cache under the temp directory, like
599*523fa7a6SAndroid Build Coastguard Worker        # "pip-out/temp.<plat>/cmake-out".
600*523fa7a6SAndroid Build Coastguard Worker        cmake_cache_dir = os.path.join(repo_root, self.build_temp, "cmake-out")
601*523fa7a6SAndroid Build Coastguard Worker        self.mkpath(cmake_cache_dir)
602*523fa7a6SAndroid Build Coastguard Worker
603*523fa7a6SAndroid Build Coastguard Worker        # Generate the cmake cache from scratch to ensure that the cache state
604*523fa7a6SAndroid Build Coastguard Worker        # is predictable.
605*523fa7a6SAndroid Build Coastguard Worker        cmake_cache_file = Path(cmake_cache_dir) / "CMakeCache.txt"
606*523fa7a6SAndroid Build Coastguard Worker        log.info(f"deleting {cmake_cache_file}")
607*523fa7a6SAndroid Build Coastguard Worker        if not self.dry_run:
608*523fa7a6SAndroid Build Coastguard Worker            # Dry run should log the command but not actually run it.
609*523fa7a6SAndroid Build Coastguard Worker            (Path(cmake_cache_dir) / "CMakeCache.txt").unlink(missing_ok=True)
610*523fa7a6SAndroid Build Coastguard Worker        with Buck2EnvironmentFixer():
611*523fa7a6SAndroid Build Coastguard Worker            # The context manager may patch the environment while running this
612*523fa7a6SAndroid Build Coastguard Worker            # cmake command, which happens to run buck2 to get some source
613*523fa7a6SAndroid Build Coastguard Worker            # lists.
614*523fa7a6SAndroid Build Coastguard Worker
615*523fa7a6SAndroid Build Coastguard Worker            # Generate the build system files.
616*523fa7a6SAndroid Build Coastguard Worker            self.spawn(["cmake", "-S", repo_root, "-B", cmake_cache_dir, *cmake_args])
617*523fa7a6SAndroid Build Coastguard Worker
618*523fa7a6SAndroid Build Coastguard Worker        # Build the system.
619*523fa7a6SAndroid Build Coastguard Worker        self.spawn(["cmake", "--build", cmake_cache_dir, *build_args])
620*523fa7a6SAndroid Build Coastguard Worker
621*523fa7a6SAndroid Build Coastguard Worker        # Non-python files should live under this data directory.
622*523fa7a6SAndroid Build Coastguard Worker        data_root = os.path.join(self.build_lib, "executorch", "data")
623*523fa7a6SAndroid Build Coastguard Worker
624*523fa7a6SAndroid Build Coastguard Worker        # Directories like bin/ and lib/ live under data/.
625*523fa7a6SAndroid Build Coastguard Worker        bin_dir = os.path.join(data_root, "bin")
626*523fa7a6SAndroid Build Coastguard Worker
627*523fa7a6SAndroid Build Coastguard Worker        # Copy the bin wrapper so that users can run any executables under
628*523fa7a6SAndroid Build Coastguard Worker        # data/bin, as long as they are listed in the [project.scripts] section
629*523fa7a6SAndroid Build Coastguard Worker        # of pyproject.toml.
630*523fa7a6SAndroid Build Coastguard Worker        self.mkpath(bin_dir)
631*523fa7a6SAndroid Build Coastguard Worker        self.copy_file(
632*523fa7a6SAndroid Build Coastguard Worker            "build/pip_data_bin_init.py.in",
633*523fa7a6SAndroid Build Coastguard Worker            os.path.join(bin_dir, "__init__.py"),
634*523fa7a6SAndroid Build Coastguard Worker        )
635*523fa7a6SAndroid Build Coastguard Worker        # Share the cmake-out location with _BaseExtension.
636*523fa7a6SAndroid Build Coastguard Worker        self.cmake_cache_dir = cmake_cache_dir
637*523fa7a6SAndroid Build Coastguard Worker
638*523fa7a6SAndroid Build Coastguard Worker        # Finally, run the underlying subcommands like build_py, build_ext.
639*523fa7a6SAndroid Build Coastguard Worker        build.run(self)
640*523fa7a6SAndroid Build Coastguard Worker
641*523fa7a6SAndroid Build Coastguard Worker
642*523fa7a6SAndroid Build Coastguard Workerdef get_ext_modules() -> List[Extension]:
643*523fa7a6SAndroid Build Coastguard Worker    """Returns the set of extension modules to build."""
644*523fa7a6SAndroid Build Coastguard Worker    ext_modules = []
645*523fa7a6SAndroid Build Coastguard Worker    if ShouldBuild.flatc():
646*523fa7a6SAndroid Build Coastguard Worker        ext_modules.append(
647*523fa7a6SAndroid Build Coastguard Worker            BuiltFile(
648*523fa7a6SAndroid Build Coastguard Worker                src_dir="third-party/flatbuffers/%BUILD_TYPE%/",
649*523fa7a6SAndroid Build Coastguard Worker                src_name="flatc",
650*523fa7a6SAndroid Build Coastguard Worker                dst="executorch/data/bin/",
651*523fa7a6SAndroid Build Coastguard Worker                is_executable=True,
652*523fa7a6SAndroid Build Coastguard Worker            )
653*523fa7a6SAndroid Build Coastguard Worker        )
654*523fa7a6SAndroid Build Coastguard Worker
655*523fa7a6SAndroid Build Coastguard Worker    if ShouldBuild.pybindings():
656*523fa7a6SAndroid Build Coastguard Worker        ext_modules.append(
657*523fa7a6SAndroid Build Coastguard Worker            # Install the prebuilt pybindings extension wrapper for the runtime,
658*523fa7a6SAndroid Build Coastguard Worker            # portable kernels, and a selection of backends. This lets users
659*523fa7a6SAndroid Build Coastguard Worker            # load and execute .pte files from python.
660*523fa7a6SAndroid Build Coastguard Worker            BuiltExtension(
661*523fa7a6SAndroid Build Coastguard Worker                "_portable_lib.*", "executorch.extension.pybindings._portable_lib"
662*523fa7a6SAndroid Build Coastguard Worker            )
663*523fa7a6SAndroid Build Coastguard Worker        )
664*523fa7a6SAndroid Build Coastguard Worker    if ShouldBuild.llama_custom_ops():
665*523fa7a6SAndroid Build Coastguard Worker        ext_modules.append(
666*523fa7a6SAndroid Build Coastguard Worker            BuiltFile(
667*523fa7a6SAndroid Build Coastguard Worker                src_dir="extension/llm/custom_ops/%BUILD_TYPE%/",
668*523fa7a6SAndroid Build Coastguard Worker                src_name="custom_ops_aot_lib",
669*523fa7a6SAndroid Build Coastguard Worker                dst="executorch/extension/llm/custom_ops",
670*523fa7a6SAndroid Build Coastguard Worker                is_dynamic_lib=True,
671*523fa7a6SAndroid Build Coastguard Worker            )
672*523fa7a6SAndroid Build Coastguard Worker        )
673*523fa7a6SAndroid Build Coastguard Worker        ext_modules.append(
674*523fa7a6SAndroid Build Coastguard Worker            # Install the prebuilt library for quantized ops required by custom ops.
675*523fa7a6SAndroid Build Coastguard Worker            BuiltFile(
676*523fa7a6SAndroid Build Coastguard Worker                src_dir="kernels/quantized/%BUILD_TYPE%/",
677*523fa7a6SAndroid Build Coastguard Worker                src_name="quantized_ops_aot_lib",
678*523fa7a6SAndroid Build Coastguard Worker                dst="executorch/kernels/quantized/",
679*523fa7a6SAndroid Build Coastguard Worker                is_dynamic_lib=True,
680*523fa7a6SAndroid Build Coastguard Worker            )
681*523fa7a6SAndroid Build Coastguard Worker        )
682*523fa7a6SAndroid Build Coastguard Worker
683*523fa7a6SAndroid Build Coastguard Worker    # Note that setuptools uses the presence of ext_modules as the main signal
684*523fa7a6SAndroid Build Coastguard Worker    # that a wheel is platform-specific. If we install any platform-specific
685*523fa7a6SAndroid Build Coastguard Worker    # files, this list must be non-empty. Therefore, we should always install
686*523fa7a6SAndroid Build Coastguard Worker    # platform-specific files using InstallerBuildExt.
687*523fa7a6SAndroid Build Coastguard Worker    return ext_modules
688*523fa7a6SAndroid Build Coastguard Worker
689*523fa7a6SAndroid Build Coastguard Worker
690*523fa7a6SAndroid Build Coastguard Worker# Override extension suffix to be ".so", skipping package info such as
691*523fa7a6SAndroid Build Coastguard Worker# "cpython-311-darwin"
692*523fa7a6SAndroid Build Coastguard Workeros.environ["SETUPTOOLS_EXT_SUFFIX"] = ".so"
693*523fa7a6SAndroid Build Coastguard Worker
694*523fa7a6SAndroid Build Coastguard Workersetup(
695*523fa7a6SAndroid Build Coastguard Worker    version=Version.string(),
696*523fa7a6SAndroid Build Coastguard Worker    # TODO(dbort): Could use py_modules to restrict the set of modules we
697*523fa7a6SAndroid Build Coastguard Worker    # package, and package_data to restrict the set up non-python files we
698*523fa7a6SAndroid Build Coastguard Worker    # include. See also setuptools/discovery.py for custom finders.
699*523fa7a6SAndroid Build Coastguard Worker    package_dir={
700*523fa7a6SAndroid Build Coastguard Worker        "executorch/backends": "backends",
701*523fa7a6SAndroid Build Coastguard Worker        # TODO(mnachin T180504136): Do not put examples/models
702*523fa7a6SAndroid Build Coastguard Worker        # into core pip packages. Refactor out the necessary utils
703*523fa7a6SAndroid Build Coastguard Worker        # or core models files into a separate package.
704*523fa7a6SAndroid Build Coastguard Worker        "executorch/examples/models": "examples/models",
705*523fa7a6SAndroid Build Coastguard Worker        "executorch/exir": "exir",
706*523fa7a6SAndroid Build Coastguard Worker        "executorch/extension": "extension",
707*523fa7a6SAndroid Build Coastguard Worker        "executorch/kernels/quantized": "kernels/quantized",
708*523fa7a6SAndroid Build Coastguard Worker        "executorch/schema": "schema",
709*523fa7a6SAndroid Build Coastguard Worker        "executorch/devtools": "devtools",
710*523fa7a6SAndroid Build Coastguard Worker        "executorch/devtools/bundled_program": "devtools/bundled_program",
711*523fa7a6SAndroid Build Coastguard Worker        "executorch/runtime": "runtime",
712*523fa7a6SAndroid Build Coastguard Worker        "executorch/util": "util",
713*523fa7a6SAndroid Build Coastguard Worker        # Note: This will install a top-level module called "serializer",
714*523fa7a6SAndroid Build Coastguard Worker        # which seems too generic and might conflict with other pip packages.
715*523fa7a6SAndroid Build Coastguard Worker        "serializer": "backends/arm/third-party/serialization_lib/python/serializer",
716*523fa7a6SAndroid Build Coastguard Worker        "tosa": "backends/arm/third-party/serialization_lib/python/tosa",
717*523fa7a6SAndroid Build Coastguard Worker    },
718*523fa7a6SAndroid Build Coastguard Worker    cmdclass={
719*523fa7a6SAndroid Build Coastguard Worker        "build": CustomBuild,
720*523fa7a6SAndroid Build Coastguard Worker        "build_ext": InstallerBuildExt,
721*523fa7a6SAndroid Build Coastguard Worker        "build_py": CustomBuildPy,
722*523fa7a6SAndroid Build Coastguard Worker    },
723*523fa7a6SAndroid Build Coastguard Worker    ext_modules=get_ext_modules(),
724*523fa7a6SAndroid Build Coastguard Worker)
725