48
48
49
49
import contextlib
50
50
import os
51
+ import platform
51
52
import re
52
53
import sys
53
54
@@ -162,6 +163,31 @@ def write_to_python_file(cls, path: str) -> None:
162
163
fp .write ("\n " .join (lines ) + "\n " )
163
164
164
165
166
+ # The build type is determined by the DEBUG environment variable. If DEBUG is
167
+ # set to a non-empty value, the build type is Debug. Otherwise, the build type
168
+ # is Release.
169
+ def get_build_type (is_debug = None ) -> str :
170
+ debug = int (os .environ .get ("DEBUG" , 0 )) if is_debug is None else is_debug
171
+ cfg = "Debug" if debug else "Release"
172
+ return cfg
173
+
174
+
175
+ def get_dynamic_lib_name (name : str ) -> str :
176
+ if platform .system () == "Windows" :
177
+ return name + ".dll"
178
+ elif platform .system () == "Darwin" :
179
+ return "lib" + name + ".dylib"
180
+ else :
181
+ return "lib" + name + ".so"
182
+
183
+
184
+ def get_executable_name (name : str ) -> str :
185
+ if platform .system () == "Windows" :
186
+ return name + ".exe"
187
+ else :
188
+ return name
189
+
190
+
165
191
class _BaseExtension (Extension ):
166
192
"""A base class that maps an abstract source to an abstract destination."""
167
193
@@ -189,9 +215,17 @@ def src_path(self, installer: "InstallerBuildExt") -> Path:
189
215
installer: The InstallerBuildExt instance that is installing the
190
216
file.
191
217
"""
192
- # TODO(dbort): share the cmake-out location with CustomBuild. Can get a
193
- # handle with installer.get_finalized_command('build')
194
- cmake_cache_dir : Path = Path ().cwd () / installer .build_temp / "cmake-out"
218
+ # Share the cmake-out location with CustomBuild.
219
+ cmake_cache_dir = Path (installer .get_finalized_command ("build" ).cmake_cache_dir )
220
+
221
+ cfg = get_build_type (installer .debug )
222
+
223
+ if os .name == "nt" :
224
+ # Replace %BUILD_TYPE% with the current build type.
225
+ self .src = self .src .replace ("%BUILD_TYPE%" , cfg )
226
+ else :
227
+ # Remove %BUILD_TYPE% from the path.
228
+ self .src = self .src .replace ("/%BUILD_TYPE%" , "" )
195
229
196
230
# Construct the full source path, resolving globs. If there are no glob
197
231
# pattern characters, this will just ensure that the source file exists.
@@ -212,17 +246,39 @@ class BuiltFile(_BaseExtension):
212
246
`ext_modules`.
213
247
"""
214
248
215
- def __init__ (self , src : str , dst : str ):
249
+ def __init__ (
250
+ self ,
251
+ src_dir : str ,
252
+ src_name : str ,
253
+ dst : str ,
254
+ is_executable : bool = False ,
255
+ is_dynamic_lib : bool = False ,
256
+ ):
216
257
"""Initializes a BuiltFile.
217
258
218
259
Args:
219
- src: The path to the file to install, relative to the cmake-out
220
- directory. May be an fnmatch-style glob that matches exactly one
221
- file.
260
+ src_dir: The directory of the file to install, relative to the cmake-out
261
+ directory. A placeholder %BUILD_TYPE% will be replaced with the build
262
+ type for multi-config generators (like Visual Studio) where the build
263
+ output is in a subdirectory named after the build type. For single-
264
+ config generators (like Makefile Generators or Ninja), this placeholder
265
+ will be removed.
266
+ src_name: The name of the file to install
222
267
dst: The path to install to, relative to the root of the pip
223
268
package. If dst ends in "/", it is treated as a directory.
224
269
Otherwise it is treated as a filename.
270
+ is_executable: If True, the file is an executable. This is used to
271
+ determine the destination filename for executable.
272
+ is_dynamic_lib: If True, the file is a dynamic library. This is used
273
+ to determine the destination filename for dynamic library.
225
274
"""
275
+ if is_executable and is_dynamic_lib :
276
+ raise ValueError ("is_executable and is_dynamic_lib cannot be both True." )
277
+ if is_executable :
278
+ src_name = get_executable_name (src_name )
279
+ elif is_dynamic_lib :
280
+ src_name = get_dynamic_lib_name (src_name )
281
+ src = os .path .join (src_dir , src_name )
226
282
# This is not a real extension, so use a unique name that doesn't look
227
283
# like a module path. Some of setuptools's autodiscovery will look for
228
284
# extension names with prefixes that match certain module paths.
@@ -397,7 +453,7 @@ def __init__(self):
397
453
self .saved_env = {}
398
454
399
455
def __enter__ (self ):
400
- if os .geteuid () == 0 and "HOME" in os .environ :
456
+ if os .name != "nt" and os . geteuid () == 0 and "HOME" in os .environ :
401
457
log .info ("temporarily unsetting HOME while running as root" )
402
458
self .saved_env ["HOME" ] = os .environ .pop ("HOME" )
403
459
return self
@@ -432,8 +488,7 @@ def initialize_options(self):
432
488
def run (self ):
433
489
self .dump_options ()
434
490
435
- debug = int (os .environ .get ("DEBUG" , 0 )) if self .debug is None else self .debug
436
- cfg = "Debug" if debug else "Release"
491
+ cfg = get_build_type (self .debug )
437
492
438
493
# get_python_lib() typically returns the path to site-packages, where
439
494
# all pip packages in the environment are installed.
@@ -508,6 +563,14 @@ def run(self):
508
563
item for item in os .environ ["CMAKE_BUILD_ARGS" ].split (" " ) if item
509
564
]
510
565
566
+ # CMAKE_BUILD_TYPE variable specifies the build type (configuration) for
567
+ # single-configuration generators (e.g., Makefile Generators or Ninja).
568
+ # For multi-config generators (like Visual Studio), CMAKE_BUILD_TYPE
569
+ # isn’t directly applicable.
570
+ # During the build step, --config specifies the configuration to build
571
+ # for multi-config generators.
572
+ build_args += ["--config" , cfg ]
573
+
511
574
# Put the cmake cache under the temp directory, like
512
575
# "pip-out/temp.<plat>/cmake-out".
513
576
cmake_cache_dir = os .path .join (repo_root , self .build_temp , "cmake-out" )
@@ -545,18 +608,24 @@ def run(self):
545
608
"build/pip_data_bin_init.py.in" ,
546
609
os .path .join (bin_dir , "__init__.py" ),
547
610
)
611
+ # Share the cmake-out location with _BaseExtension.
612
+ self .cmake_cache_dir = cmake_cache_dir
548
613
549
614
# Finally, run the underlying subcommands like build_py, build_ext.
550
615
build .run (self )
551
616
552
617
553
618
def get_ext_modules () -> List [Extension ]:
554
619
"""Returns the set of extension modules to build."""
555
-
556
620
ext_modules = []
557
621
if ShouldBuild .flatc ():
558
622
ext_modules .append (
559
- BuiltFile ("third-party/flatbuffers/flatc" , "executorch/data/bin/" )
623
+ BuiltFile (
624
+ src_dir = "third-party/flatbuffers/%BUILD_TYPE%/" ,
625
+ src_name = "flatc" ,
626
+ dst = "executorch/data/bin/" ,
627
+ is_executable = True ,
628
+ )
560
629
)
561
630
562
631
if ShouldBuild .pybindings ():
@@ -570,17 +639,20 @@ def get_ext_modules() -> List[Extension]:
570
639
)
571
640
if ShouldBuild .llama_custom_ops ():
572
641
ext_modules .append (
573
- # Install the prebuilt library for custom ops used in llama.
574
642
BuiltFile (
575
- "extension/llm/custom_ops/libcustom_ops_aot_lib.*" ,
576
- "executorch/extension/llm/custom_ops/" ,
643
+ src_dir = "extension/llm/custom_ops/%BUILD_TYPE%/" ,
644
+ src_name = "custom_ops_aot_lib" ,
645
+ dst = "executorch/extension/llm/custom_ops" ,
646
+ is_dynamic_lib = True ,
577
647
)
578
648
)
579
649
ext_modules .append (
580
650
# Install the prebuilt library for quantized ops required by custom ops.
581
651
BuiltFile (
582
- "kernels/quantized/libquantized_ops_aot_lib.*" ,
583
- "executorch/kernels/quantized/" ,
652
+ src_dir = "kernels/quantized/%BUILD_TYPE%/" ,
653
+ src_name = "quantized_ops_aot_lib" ,
654
+ dst = "executorch/kernels/quantized/" ,
655
+ is_dynamic_lib = True ,
584
656
)
585
657
)
586
658
0 commit comments