Skip to content

Commit 0ec8ef5

Browse files
committed
[Build] Support windows in setup.py
Fix windows build issues in setup.py 1. Build executable name and dynamic lib name based on platform. 2. Set the src directory based on build config for windows. 3. Avoid using os.geteuid() on windows. For pytorch#4661
1 parent 02a76ea commit 0ec8ef5

File tree

1 file changed

+89
-17
lines changed

1 file changed

+89
-17
lines changed

setup.py

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848

4949
import contextlib
5050
import os
51+
import platform
5152
import re
5253
import sys
5354

@@ -162,6 +163,31 @@ def write_to_python_file(cls, path: str) -> None:
162163
fp.write("\n".join(lines) + "\n")
163164

164165

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+
165191
class _BaseExtension(Extension):
166192
"""A base class that maps an abstract source to an abstract destination."""
167193

@@ -189,9 +215,17 @@ def src_path(self, installer: "InstallerBuildExt") -> Path:
189215
installer: The InstallerBuildExt instance that is installing the
190216
file.
191217
"""
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%", "")
195229

196230
# Construct the full source path, resolving globs. If there are no glob
197231
# pattern characters, this will just ensure that the source file exists.
@@ -212,17 +246,39 @@ class BuiltFile(_BaseExtension):
212246
`ext_modules`.
213247
"""
214248

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+
):
216257
"""Initializes a BuiltFile.
217258
218259
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
222267
dst: The path to install to, relative to the root of the pip
223268
package. If dst ends in "/", it is treated as a directory.
224269
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.
225274
"""
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)
226282
# This is not a real extension, so use a unique name that doesn't look
227283
# like a module path. Some of setuptools's autodiscovery will look for
228284
# extension names with prefixes that match certain module paths.
@@ -397,7 +453,7 @@ def __init__(self):
397453
self.saved_env = {}
398454

399455
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:
401457
log.info("temporarily unsetting HOME while running as root")
402458
self.saved_env["HOME"] = os.environ.pop("HOME")
403459
return self
@@ -432,8 +488,7 @@ def initialize_options(self):
432488
def run(self):
433489
self.dump_options()
434490

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)
437492

438493
# get_python_lib() typically returns the path to site-packages, where
439494
# all pip packages in the environment are installed.
@@ -508,6 +563,14 @@ def run(self):
508563
item for item in os.environ["CMAKE_BUILD_ARGS"].split(" ") if item
509564
]
510565

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+
511574
# Put the cmake cache under the temp directory, like
512575
# "pip-out/temp.<plat>/cmake-out".
513576
cmake_cache_dir = os.path.join(repo_root, self.build_temp, "cmake-out")
@@ -545,18 +608,24 @@ def run(self):
545608
"build/pip_data_bin_init.py.in",
546609
os.path.join(bin_dir, "__init__.py"),
547610
)
611+
# Share the cmake-out location with _BaseExtension.
612+
self.cmake_cache_dir = cmake_cache_dir
548613

549614
# Finally, run the underlying subcommands like build_py, build_ext.
550615
build.run(self)
551616

552617

553618
def get_ext_modules() -> List[Extension]:
554619
"""Returns the set of extension modules to build."""
555-
556620
ext_modules = []
557621
if ShouldBuild.flatc():
558622
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+
)
560629
)
561630

562631
if ShouldBuild.pybindings():
@@ -570,17 +639,20 @@ def get_ext_modules() -> List[Extension]:
570639
)
571640
if ShouldBuild.llama_custom_ops():
572641
ext_modules.append(
573-
# Install the prebuilt library for custom ops used in llama.
574642
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,
577647
)
578648
)
579649
ext_modules.append(
580650
# Install the prebuilt library for quantized ops required by custom ops.
581651
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,
584656
)
585657
)
586658

0 commit comments

Comments
 (0)