49
49
import re
50
50
import sys
51
51
52
- from distutils import log
53
52
from distutils .sysconfig import get_python_lib
54
53
from pathlib import Path
55
54
62
61
# https://setuptools.pypa.io/en/latest/userguide/extension.html
63
62
64
63
64
+ class ShouldBuild :
65
+ """Indicates whether to build various components."""
66
+
67
+ @staticmethod
68
+ def _is_env_enabled (env_var : str , default : bool = False ) -> bool :
69
+ val = os .environ .get (env_var , None )
70
+ if val is None :
71
+ return default
72
+ if val in ("OFF" , "0" , "" ):
73
+ return False
74
+ return True
75
+
76
+ @classmethod
77
+ @property
78
+ def pybindings (cls ) -> bool :
79
+ return cls ._is_env_enabled ("EXECUTORCH_BUILD_PYBIND" , default = False )
80
+
81
+ @classmethod
82
+ @property
83
+ def xnnpack (cls ) -> bool :
84
+ return cls ._is_env_enabled ("EXECUTORCH_BUILD_XNNPACK" , default = False )
85
+
86
+ @classmethod
87
+ @property
88
+ def aot_util (cls ) -> bool :
89
+ return cls ._is_env_enabled ("EXECUTORCH_BUILD_AOT_UTIL" , default = True )
90
+
91
+
65
92
class _BaseExtension (Extension ):
66
93
"""A base class that maps an abstract source to an abstract destination."""
67
94
@@ -89,8 +116,8 @@ def src_path(self, installer: "InstallerBuildExt") -> Path:
89
116
installer: The InstallerBuildExt instance that is installing the
90
117
file.
91
118
"""
92
- # @nocommit : share the cmake-out location with CustomBuild.
93
- # Can get a handle with installer.get_finalized_command('build')
119
+ # TODO(dbort) : share the cmake-out location with CustomBuild. Can get a
120
+ # handle with installer.get_finalized_command('build')
94
121
cmake_cache_dir : Path = Path ().cwd () / installer .build_temp / "cmake-out"
95
122
96
123
# Construct the full source path, resolving globs. If there are no glob
@@ -104,7 +131,13 @@ def src_path(self, installer: "InstallerBuildExt") -> Path:
104
131
105
132
106
133
class BuiltFile (_BaseExtension ):
107
- """An extension that installs a single file that was built by cmake."""
134
+ """An extension that installs a single file that was built by cmake.
135
+
136
+ This isn't technically a `build_ext` style python extension, but there's no
137
+ dedicated command for installing arbitrary data. It's convenient to use
138
+ this, though, because it lets us manage the files to install as entries in
139
+ `ext_modules`.
140
+ """
108
141
109
142
def __init__ (self , src : str , dst : str ):
110
143
"""Initializes a BuiltFile.
@@ -149,7 +182,8 @@ def __init__(self, src: str, modpath: str):
149
182
Args:
150
183
src: The path to the file to install (typically a shared library),
151
184
relative to the cmake-out directory. May be an fnmatch-style
152
- glob that matches exactly one file.
185
+ glob that matches exactly one file. If the path ends in `.so`,
186
+ this class will also look for similarly-named `.dylib` files.
153
187
modpath: The dotted path of the python module that maps to the
154
188
extension.
155
189
"""
@@ -193,6 +227,8 @@ def dst_path(self, installer: "InstallerBuildExt") -> Path:
193
227
194
228
195
229
class InstallerBuildExt (build_ext ):
230
+ """Installs files that were built by cmake."""
231
+
196
232
# TODO(dbort): Depend on the "build" command to ensure it runs first
197
233
198
234
def build_extension (self , ext : _BaseExtension ) -> None :
@@ -227,12 +263,6 @@ class CustomBuildPy(build_py):
227
263
"""
228
264
229
265
def run (self ):
230
- if False : # @nocommit
231
- self .no_compile = "NO_COMPILE"
232
- self .dump_options () # @nocommit
233
- print ("STOP" )
234
- sys .exit (1 )
235
-
236
266
# Copy python files to the output directory. This set of files is
237
267
# defined by the py_module list and package_data patterns.
238
268
build_py .run (self )
@@ -297,7 +327,7 @@ def initialize_options(self):
297
327
self .parallel = os .environ .get ("CMAKE_BUILD_PARALLEL_LEVEL" , "9" )
298
328
299
329
def run (self ):
300
- self .dump_options () # @nocommit
330
+ self .dump_options ()
301
331
302
332
debug = int (os .environ .get ("DEBUG" , 0 )) if self .debug is None else self .debug
303
333
cfg = "Debug" if debug else "Release"
@@ -322,20 +352,18 @@ def run(self):
322
352
f"-DCMAKE_BUILD_TYPE={ cfg } " ,
323
353
]
324
354
325
- # TODO(dbort): Separate build target options from the base cmake setup.
326
- # @nocommit: Don't repeat the " build aot_util" env logic
327
- if os . environ . get ( "EXECUTORCH_BUILD_AOT_UTIL" , "ON" ) == "ON" :
355
+ # TODO(dbort): This block should also assemble the list of targets to
356
+ # build so we only build what we need.
357
+ if ShouldBuild . aot_util :
328
358
cmake_args += [
329
359
"-DEXECUTORCH_BUILD_EXTENSION_AOT_UTIL=ON" ,
330
360
]
331
- # @nocommit: Don't repeat the "build pybind" env logic
332
- if os .environ .get ("EXECUTORCH_BUILD_PYBIND" , "OFF" ) == "ON" :
361
+ if ShouldBuild .pybindings :
333
362
cmake_args += [
334
363
"-DEXECUTORCH_BUILD_PYBIND=ON" ,
335
364
]
336
- if os . environ . get ( "EXECUTORCH_BUILD_XNNPACK" , None ) :
365
+ if ShouldBuild . xnnpack :
337
366
cmake_args += [
338
- # @nocommit: fails in the xnnpack build files
339
367
"-DEXECUTORCH_BUILD_XNNPACK=ON" ,
340
368
]
341
369
# TODO(dbort): Add MPS/CoreML backends when building on macos.
@@ -348,10 +376,6 @@ def run(self):
348
376
349
377
build_args = [f"-j{ self .parallel } " ]
350
378
351
- self .dry_run = False # @nocommit
352
- if self .dry_run :
353
- log .info ("CustomBuild: DRY_RUN" )
354
-
355
379
# Put the cmake cache under the temp directory, like
356
380
# "pip-out/temp.<plat>/cmake-out".
357
381
cmake_cache_dir = os .path .join (repo_root , self .build_temp , "cmake-out" )
@@ -363,33 +387,19 @@ def run(self):
363
387
# Build the system.
364
388
self .spawn (["cmake" , "--build" , cmake_cache_dir , * build_args ])
365
389
366
- # Install some files to a common spot under cmake-out. This will make
367
- # them available install via BuiltExtension with a source under
368
- # `_installed/`.
369
- # TODO(dbort): Add --strip for release builds.
370
- cmake_install_dir = os .path .join (cmake_cache_dir , "_installed" )
371
- self .spawn (
372
- [
373
- "cmake" ,
374
- "--install" ,
375
- cmake_cache_dir ,
376
- "--prefix" ,
377
- cmake_install_dir ,
378
- ]
379
- )
380
-
381
- # Copy all installed files into the package, under data/. If names or
382
- # locations need to change, handle that in the CMake config instead of
383
- # adding a mapping here.
390
+ # Non-python files should live under this data directory.
384
391
data_root = os .path .join (self .build_lib , "executorch" , "data" )
385
- self .copy_tree (cmake_install_dir , data_root )
392
+
393
+ # Directories like bin/ and lib/ live under data/.
394
+ bin_dir = os .path .join (data_root , "bin" )
386
395
387
396
# Copy the bin wrapper so that users can run any executables under
388
397
# data/bin, as long as they are listed in the [project.scripts] section
389
398
# of pyproject.toml.
399
+ self .mkpath (bin_dir )
390
400
self .copy_file (
391
401
"build/pip_data_bin_init.py.in" ,
392
- os .path .join (data_root , "bin" , "__init__.py" ),
402
+ os .path .join (bin_dir , "__init__.py" ),
393
403
)
394
404
395
405
# Finally, run the underlying subcommands like build_py, build_ext.
@@ -400,19 +410,16 @@ def get_ext_modules() -> list[Extension]:
400
410
"""Returns the set of extension modules to build."""
401
411
402
412
ext_modules = [
403
- # @nocommit: remove this since now it's installed by the build command.
404
- # but removing this requires potentially adding a no-op extension to
405
- # mark the wheel as non-pure, when we're not building the pybindings.
406
413
BuiltFile ("third-party/flatbuffers/flatc" , "executorch/data/bin/" ),
407
414
]
408
- if os . environ . get ( "EXECUTORCH_BUILD_AOT_UTIL" , "ON" ) == "ON" :
415
+ if ShouldBuild . aot_util :
409
416
ext_modules .append (
410
417
BuiltExtension (
411
418
"extension/aot_util/libaot_util.so" ,
412
419
"executorch.extension.aot_util.aot_util" ,
413
420
)
414
421
)
415
- if os . environ . get ( "EXECUTORCH_BUILD_PYBIND" , "OFF" ) == "ON" :
422
+ if ShouldBuild . pybindings :
416
423
ext_modules .append (
417
424
# Install the prebuilt pybindings extension wrapper for the runtime,
418
425
# portable kernels, and a selection of backends. This lets users
@@ -445,7 +452,7 @@ def get_ext_modules() -> list[Extension]:
445
452
"executorch/sdk" : "sdk" ,
446
453
"executorch/sdk/bundled_program" : "sdk/bundled_program" ,
447
454
"executorch/util" : "util" ,
448
- # @nocommit : This will install a top-level module called "serializer",
455
+ # Note : This will install a top-level module called "serializer",
449
456
# which seems too generic and might conflict with other pip packages.
450
457
"serializer" : "backends/arm/third-party/serialization_lib/python/serializer" ,
451
458
"tosa" : "backends/arm/third-party/serialization_lib/python/tosa" ,
0 commit comments