@@ -220,37 +220,50 @@ def src_path(self, installer: "InstallerBuildExt") -> Path:
220220 """
221221 # Share the cmake-out location with CustomBuild.
222222 build_cmd = installer .get_finalized_command ("build" )
223- if hasattr (build_cmd , "cmake_cache_dir" ):
224- cmake_cache_dir = Path (build_cmd .cmake_cache_dir )
223+ if "%CMAKE_CACHE_DIR%" in self .src :
224+ if not hasattr (build_cmd , "cmake_cache_dir" ):
225+ raise RuntimeError (
226+ f"Extension { self .name } has a src { self .src } that contains"
227+ " %CMAKE_CACHE_DIR% but CMake does not run in the `build` "
228+ "command. Please double check if the command is correct."
229+ )
230+ else :
231+ build_dir = Path (build_cmd .cmake_cache_dir )
225232 else :
226- # If we're in editable mode, use a default or fallback value for cmake_cache_dir
227- # This could be a hardcoded path, or a path derived from the current working directory
228- cmake_cache_dir = Path ("." )
233+ # If the src path doesn't contain %CMAKE_CACHE_DIR% placeholder,
234+ # try to find it under the current directory.
235+ build_dir = Path ("." )
236+
237+ src_path = self .src .replace ("%CMAKE_CACHE_DIR%/" , "" )
238+
229239 cfg = get_build_type (installer .debug )
230240
231241 if os .name == "nt" :
232242 # Replace %BUILD_TYPE% with the current build type.
233- self . src = self . src .replace ("%BUILD_TYPE%" , cfg )
243+ src_path = src_path .replace ("%BUILD_TYPE%" , cfg )
234244 else :
235245 # Remove %BUILD_TYPE% from the path.
236- self . src = self . src .replace ("/%BUILD_TYPE%" , "" )
246+ src_path = src_path .replace ("/%BUILD_TYPE%" , "" )
237247
238248 # Construct the full source path, resolving globs. If there are no glob
239249 # pattern characters, this will just ensure that the source file exists.
240- srcs = tuple (cmake_cache_dir .glob (self . src ))
250+ srcs = tuple (build_dir .glob (src_path ))
241251 if len (srcs ) != 1 :
242252 raise ValueError (
243- f"""Expected exactly one file matching '{ self .src } ' in { cmake_cache_dir } ; found { repr (srcs )} .
244-
245- If that file is a CMake-built extension module file, and we are installing in editable mode, please disable the corresponding build option since it's not supported yet.
246-
247- Try:
248-
249- EXECUTORCH_BUILD_FLATC=OFF EXECUTORCH_BUILD_KERNELS_CUSTOM_AOT=OFF pip install -e .
250- """
253+ f"Expecting exactly 1 file matching { self .src } in { build_dir } , "
254+ f"found { repr (srcs )} . Resolved src pattern: { src_path } ."
251255 )
252256 return srcs [0 ]
253257
258+ def inplace_dir (self , installer : "InstallerBuildExt" ) -> Path :
259+ """Returns the path of this file to be installed to, under inplace mode.
260+
261+ It will be a relative path to the project root directory. For more info
262+ related to inplace/editable mode, please checkout this doc:
263+ https://setuptools.pypa.io/en/latest/userguide/development_mode.html
264+ """
265+ raise NotImplementedError ()
266+
254267
255268class BuiltFile (_BaseExtension ):
256269 """An extension that installs a single file that was built by cmake.
@@ -316,6 +329,18 @@ def dst_path(self, installer: "InstallerBuildExt") -> Path:
316329 # Destination looks like a file.
317330 return dst_root / Path (self .dst )
318331
332+ def inplace_dir (self , installer : "InstallerBuildExt" ) -> Path :
333+ """For a `BuiltFile`, we use self.dst as its inplace directory path.
334+ Need to handle directory vs file.
335+ """
336+ # HACK: get rid of the leading "executorch" in ext.dst.
337+ # This is because we don't have a root level "executorch" module.
338+ package_dir = self .dst .removeprefix ("executorch/" )
339+ # If dst is a file, use it's directory
340+ if not package_dir .endswith ("/" ):
341+ package_dir = os .path .dirname (package_dir )
342+ return Path (package_dir )
343+
319344
320345class BuiltExtension (_BaseExtension ):
321346 """An extension that installs a python extension that was built by cmake."""
@@ -369,6 +394,15 @@ def dst_path(self, installer: "InstallerBuildExt") -> Path:
369394 # path: that's the file we're creating.
370395 return Path (installer .get_ext_fullpath (self .dst ))
371396
397+ def inplace_dir (self , installer : "InstallerBuildExt" ) -> Path :
398+ """For BuiltExtension, deduce inplace dir path from extension name."""
399+ build_py = installer .get_finalized_command ("build_py" )
400+ modpath = self .name .split ("." )
401+ package = "." .join (modpath [:- 1 ])
402+ package_dir = os .path .abspath (build_py .get_package_dir (package ))
403+
404+ return Path (package_dir )
405+
372406
373407class InstallerBuildExt (build_ext ):
374408 """Installs files that were built by cmake."""
@@ -399,23 +433,15 @@ def copy_extensions_to_source(self) -> None:
399433
400434 Returns:
401435 """
402- build_py = self .get_finalized_command ("build_py" )
403436 for ext in self .extensions :
404- if isinstance (ext , BuiltExtension ):
405- modpath = ext .name .split ("." )
406- package = "." .join (modpath [:- 1 ])
407- package_dir = os .path .abspath (build_py .get_package_dir (package ))
408- else :
409- # HACK: get rid of the leading "executorch" in ext.dst.
410- # This is because we don't have a root level "executorch" module.
411- package_dir = ext .dst .removeprefix ("executorch/" )
437+ package_dir = ext .inplace_dir (self )
412438
413439 # Ensure that the destination directory exists.
414440 self .mkpath (os .fspath (package_dir ))
415441
416442 regular_file = ext .src_path (self )
417443 inplace_file = os .path .join (
418- package_dir , os .path .basename (ext .src_path (self ))
444+ package_dir , os .path .basename (ext .dst_path (self ))
419445 )
420446
421447 # Always copy, even if source is older than destination, to ensure
@@ -724,20 +750,6 @@ def run(self):
724750 # Build the system.
725751 self .spawn (["cmake" , "--build" , cmake_cache_dir , * build_args ])
726752
727- # Non-python files should live under this data directory.
728- data_root = os .path .join (self .build_lib , "executorch" , "data" )
729-
730- # Directories like bin/ and lib/ live under data/.
731- bin_dir = os .path .join (data_root , "bin" )
732-
733- # Copy the bin wrapper so that users can run any executables under
734- # data/bin, as long as they are listed in the [project.scripts] section
735- # of pyproject.toml.
736- self .mkpath (bin_dir )
737- self .copy_file (
738- "build/pip_data_bin_init.py.in" ,
739- os .path .join (bin_dir , "__init__.py" ),
740- )
741753 # Share the cmake-out location with _BaseExtension.
742754 self .cmake_cache_dir = cmake_cache_dir
743755
@@ -749,13 +761,20 @@ def get_ext_modules() -> List[Extension]:
749761 """Returns the set of extension modules to build."""
750762 ext_modules = []
751763 if ShouldBuild .flatc ():
752- ext_modules .append (
753- BuiltFile (
754- src_dir = "third-party/flatbuffers/%BUILD_TYPE%/" ,
755- src_name = "flatc" ,
756- dst = "executorch/data/bin/" ,
757- is_executable = True ,
758- )
764+ ext_modules .extend (
765+ [
766+ BuiltFile (
767+ src_dir = "%CMAKE_CACHE_DIR%/third-party/flatbuffers/%BUILD_TYPE%/" ,
768+ src_name = "flatc" ,
769+ dst = "executorch/data/bin/" ,
770+ is_executable = True ,
771+ ),
772+ BuiltFile (
773+ src_dir = "build/" ,
774+ src_name = "pip_data_bin_init.py.in" ,
775+ dst = "executorch/data/bin/__init__.py" ,
776+ ),
777+ ]
759778 )
760779
761780 if ShouldBuild .pybindings ():
@@ -778,16 +797,16 @@ def get_ext_modules() -> List[Extension]:
778797 if ShouldBuild .llama_custom_ops ():
779798 ext_modules .append (
780799 BuiltFile (
781- src_dir = "extension/llm/custom_ops/%BUILD_TYPE%/" ,
800+ src_dir = "%CMAKE_CACHE_DIR%/ extension/llm/custom_ops/%BUILD_TYPE%/" ,
782801 src_name = "custom_ops_aot_lib" ,
783- dst = "executorch/extension/llm/custom_ops" ,
802+ dst = "executorch/extension/llm/custom_ops/ " ,
784803 is_dynamic_lib = True ,
785804 )
786805 )
787806 ext_modules .append (
788807 # Install the prebuilt library for quantized ops required by custom ops.
789808 BuiltFile (
790- src_dir = "kernels/quantized/%BUILD_TYPE%/" ,
809+ src_dir = "%CMAKE_CACHE_DIR%/ kernels/quantized/%BUILD_TYPE%/" ,
791810 src_name = "quantized_ops_aot_lib" ,
792811 dst = "executorch/kernels/quantized/" ,
793812 is_dynamic_lib = True ,
0 commit comments