From 4dee533bc35f5e0db7db4cc25e941dfa833aaae0 Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Mon, 12 Aug 2024 23:07:10 -0400 Subject: [PATCH 1/4] [Build] fix build issues for native Windows. Added install_requirements.ps1 for build on native Windows. Also fix issues block build on native Windows. 1. Get the Windows version of buck2 for Windows. 2. Check the path based on the build configuration for Windows in setup.py. 3. Avoid generating 'def flat.exe()' which is illegal python. 4. Add the Windows version of kernel_link_options. 5. Add links of pthreadpool and cpuinfo for custom_ops_aot_lib. 6. Define ssize_t for Windows. Still have one link issue tracked with #4659. For #4661 --- build/Utils.cmake | 10 ++++ build/pip_data_bin_init.py.in | 4 +- build/resolve_buck.py | 17 ++++-- extension/data_loader/file_data_loader.cpp | 13 +++++ extension/llm/custom_ops/CMakeLists.txt | 3 + runtime/core/exec_aten/util/tensor_util.h | 5 ++ runtime/core/portable_type/tensor_impl.h | 5 ++ setup.py | 68 ++++++++++++++++------ 8 files changed, 101 insertions(+), 24 deletions(-) diff --git a/build/Utils.cmake b/build/Utils.cmake index 56fc1e104b0..55f5892a55e 100644 --- a/build/Utils.cmake +++ b/build/Utils.cmake @@ -143,11 +143,21 @@ function(macos_kernel_link_options target_name) ) endfunction() +# Same as kernel_link_options but it's for MSVC linker +function(msvc_kernel_link_options target_name) + target_link_options( + ${target_name} INTERFACE + "SHELL:LINKER:/WHOLEARCHIVE:$" + ) +endfunction() + # Ensure that the load-time constructor functions run. By default, the linker # would remove them since there are no other references to them. function(target_link_options_shared_lib target_name) if(APPLE) macos_kernel_link_options(${target_name}) + elseif(MSVC) + msvc_kernel_link_options(${target_name}) else() kernel_link_options(${target_name}) endif() diff --git a/build/pip_data_bin_init.py.in b/build/pip_data_bin_init.py.in index 9644c5621df..92998bfa41c 100644 --- a/build/pip_data_bin_init.py.in +++ b/build/pip_data_bin_init.py.in @@ -21,7 +21,9 @@ def _find_executable_files_under(dir): for filename in os.listdir(dir): filepath = os.path.join(dir, filename) if os.path.isfile(filepath) and os.access(filepath, os.X_OK): - bin_names.append(filename) + # avoid def flat.exe() on windows. + filename_without_ext = os.path.splitext(filename)[0] + bin_names.append(filename_without_ext) return bin_names # The list of binaries to create wrapper functions for. diff --git a/build/resolve_buck.py b/build/resolve_buck.py index d70fe94b415..804ab22a033 100644 --- a/build/resolve_buck.py +++ b/build/resolve_buck.py @@ -11,7 +11,6 @@ import platform import stat import sys -import tempfile import urllib.request from dataclasses import dataclass @@ -85,6 +84,12 @@ class BuckInfo: archive_name="buck2-x86_64-apple-darwin.zst", target_versions=["3eb1ae97ea963086866b4d2d9ffa966d"], ), + ("windows", "x86_64"): BuckInfo( + archive_name="buck2-x86_64-pc-windows-msvc.exe.zst", + target_versions=[ + "bf1685c4c4ddd9de4592b5a955cb7326fd01e6c4d5f561643422bed961a17401" + ], + ), } @@ -135,6 +140,8 @@ def resolve_buck2(args: argparse.Namespace) -> Union[str, int]: os_family = "linux" elif sys.platform.startswith("darwin"): os_family = "darwin" + elif sys.platform.startswith("win"): + os_family = "windows" platform_key = (os_family, arch) if platform_key not in BUCK_PLATFORM_MAP: @@ -193,12 +200,12 @@ def resolve_buck2(args: argparse.Namespace) -> Union[str, int]: buck2_archive_url = f"https://github.com/facebook/buck2/releases/download/{target_buck_version}/{buck_info.archive_name}" - with tempfile.NamedTemporaryFile() as archive_file: + try: print(f"Downloading buck2 from {buck2_archive_url}...", file=sys.stderr) - urllib.request.urlretrieve(buck2_archive_url, archive_file.name) + archive_file, _ = urllib.request.urlretrieve(buck2_archive_url) # Extract and chmod. - with open(archive_file.name, "rb") as f: + with open(archive_file, "rb") as f: data = f.read() decompressed_bytes = zstd.decompress(data) @@ -207,6 +214,8 @@ def resolve_buck2(args: argparse.Namespace) -> Union[str, int]: file_stat = os.stat(buck2_local_path) os.chmod(buck2_local_path, file_stat.st_mode | stat.S_IEXEC) + finally: + os.remove(archive_file) return buck2_local_path diff --git a/extension/data_loader/file_data_loader.cpp b/extension/data_loader/file_data_loader.cpp index 9e0a958ea0d..5881d3fc19e 100644 --- a/extension/data_loader/file_data_loader.cpp +++ b/extension/data_loader/file_data_loader.cpp @@ -17,7 +17,16 @@ #include #include #include +#ifndef _WIN32 #include +#else +#include +#endif + +#ifdef _WIN32 +#include +using ssize_t = ptrdiff_t; +#endif #include #include @@ -57,6 +66,10 @@ FileDataLoader::~FileDataLoader() { std::free(const_cast(file_name_)); // fd_ can be -1 if this instance was moved from, but closing a negative fd is // safe (though it will return an error). +#ifdef _WIN32 + if (fd_ == -1) + return; +#endif ::close(fd_); } diff --git a/extension/llm/custom_ops/CMakeLists.txt b/extension/llm/custom_ops/CMakeLists.txt index 3f242e3d7d7..32c10a17d1c 100644 --- a/extension/llm/custom_ops/CMakeLists.txt +++ b/extension/llm/custom_ops/CMakeLists.txt @@ -101,6 +101,9 @@ if(EXECUTORCH_BUILD_KERNELS_CUSTOM_AOT) endif() target_link_libraries(custom_ops_aot_lib PUBLIC cpublas torch) + if(WIN32) + target_link_libraries(custom_ops_aot_lib PUBLIC pthreadpool cpuinfo) + endif() target_compile_options( custom_ops_aot_lib PUBLIC -Wno-deprecated-declarations -fPIC -frtti -fexceptions diff --git a/runtime/core/exec_aten/util/tensor_util.h b/runtime/core/exec_aten/util/tensor_util.h index b18cd349a62..198b92d5d82 100644 --- a/runtime/core/exec_aten/util/tensor_util.h +++ b/runtime/core/exec_aten/util/tensor_util.h @@ -15,6 +15,11 @@ #include // size_t #include +#ifdef _WIN32 +#include +using ssize_t = ptrdiff_t; +#endif + #include #include #include diff --git a/runtime/core/portable_type/tensor_impl.h b/runtime/core/portable_type/tensor_impl.h index 09ee744ae7f..c2f6ccb83a2 100644 --- a/runtime/core/portable_type/tensor_impl.h +++ b/runtime/core/portable_type/tensor_impl.h @@ -8,7 +8,12 @@ #pragma once +#ifndef _WIN32 #include // TODO(T126923429): Include size_t, ssize_t +#else +#include +using ssize_t = ptrdiff_t; +#endif #include #include diff --git a/setup.py b/setup.py index 58a9973c9f9..5eab8167a3c 100644 --- a/setup.py +++ b/setup.py @@ -189,9 +189,9 @@ def src_path(self, installer: "InstallerBuildExt") -> Path: installer: The InstallerBuildExt instance that is installing the file. """ - # TODO(dbort): share the cmake-out location with CustomBuild. Can get a - # handle with installer.get_finalized_command('build') - cmake_cache_dir: Path = Path().cwd() / installer.build_temp / "cmake-out" + # share the cmake-out location with CustomBuild. + # Get a handle with installer.get_finalized_command('build') + cmake_cache_dir = Path(installer.get_finalized_command("build").cmake_cache_dir) # Construct the full source path, resolving globs. If there are no glob # pattern characters, this will just ensure that the source file exists. @@ -397,7 +397,7 @@ def __init__(self): self.saved_env = {} def __enter__(self): - if os.geteuid() == 0 and "HOME" in os.environ: + if os.name != "nt" and os.geteuid() == 0 and "HOME" in os.environ: log.info("temporarily unsetting HOME while running as root") self.saved_env["HOME"] = os.environ.pop("HOME") return self @@ -508,6 +508,9 @@ def run(self): item for item in os.environ["CMAKE_BUILD_ARGS"].split(" ") if item ] + if os.name == "nt": + build_args += ["--config", cfg] + # Put the cmake cache under the temp directory, like # "pip-out/temp./cmake-out". cmake_cache_dir = os.path.join(repo_root, self.build_temp, "cmake-out") @@ -545,6 +548,8 @@ def run(self): "build/pip_data_bin_init.py.in", os.path.join(bin_dir, "__init__.py"), ) + # share the cmake-out location with _BaseExtension. + self.cmake_cache_dir = cmake_cache_dir # Finally, run the underlying subcommands like build_py, build_ext. build.run(self) @@ -553,11 +558,21 @@ def run(self): def get_ext_modules() -> List[Extension]: """Returns the set of extension modules to build.""" + debug = os.environ.get("DEBUG", 0) + cfg = "Debug" if debug else "Release" + ext_modules = [] if ShouldBuild.flatc(): - ext_modules.append( - BuiltFile("third-party/flatbuffers/flatc", "executorch/data/bin/") - ) + if os.name == "nt": + ext_modules.append( + BuiltFile( + f"third-party/flatbuffers/{cfg}/flatc.exe", "executorch/data/bin/" + ) + ) + else: + ext_modules.append( + BuiltFile("third-party/flatbuffers/flatc", "executorch/data/bin/") + ) if ShouldBuild.pybindings(): ext_modules.append( @@ -569,20 +584,35 @@ def get_ext_modules() -> List[Extension]: ) ) if ShouldBuild.llama_custom_ops(): - ext_modules.append( - # Install the prebuilt library for custom ops used in llama. - BuiltFile( - "extension/llm/custom_ops/libcustom_ops_aot_lib.*", - "executorch/extension/llm/custom_ops/", + if os.name == "nt": + ext_modules.append( + BuiltFile( + f"extension/llm/custom_ops/{cfg}/custom_ops_aot_lib.dll", + "executorch/extension/llm/custom_ops", + ) ) - ) - ext_modules.append( - # Install the prebuilt library for quantized ops required by custom ops. - BuiltFile( - "kernels/quantized/libquantized_ops_aot_lib.*", - "executorch/kernels/quantized/", + ext_modules.append( + # Install the prebuilt library for quantized ops required by custom ops. + BuiltFile( + f"kernels/quantized/{cfg}/quantized_ops_aot_lib.dll", + "executorch/kernels/quantized/", + ) + ) + else: + ext_modules.append( + # Install the prebuilt library for custom ops used in llama. + BuiltFile( + "extension/llm/custom_ops/libcustom_ops_aot_lib.*", + "executorch/extension/llm/custom_ops/", + ) + ) + ext_modules.append( + # Install the prebuilt library for quantized ops required by custom ops. + BuiltFile( + "kernels/quantized/libquantized_ops_aot_lib.*", + "executorch/kernels/quantized/", + ) ) - ) # Note that setuptools uses the presence of ext_modules as the main signal # that a wheel is platform-specific. If we install any platform-specific From 8a8b9904adc0d0279bb1ac9135a8b25c7d5dff9d Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Mon, 12 Aug 2024 23:16:18 -0400 Subject: [PATCH 2/4] Fix lint --- install_requirements.ps1 | 125 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 install_requirements.ps1 diff --git a/install_requirements.ps1 b/install_requirements.ps1 new file mode 100644 index 00000000000..102121b17ac --- /dev/null +++ b/install_requirements.ps1 @@ -0,0 +1,125 @@ +# Check if the PYTHON_EXECUTABLE environment variable is set +if ([string]::IsNullOrEmpty($env:PYTHON_EXECUTABLE)) { + # Check if the CONDA_DEFAULT_ENV environment variable is empty, equal to "base", or if the "python" command is not executable + if ([string]::IsNullOrEmpty($env:CONDA_DEFAULT_ENV) -or $env:CONDA_DEFAULT_ENV -eq "base" -or !(Get-Command "python" -ErrorAction SilentlyContinue)) { + # Set PYTHON_EXECUTABLE to "python3" + $env:PYTHON_EXECUTABLE = "python3" + } + else { + # Set PYTHON_EXECUTABLE to "python" + $env:PYTHON_EXECUTABLE = "python" + } +} + +# Set the PIP_EXECUTABLE based on the value of PYTHON_EXECUTABLE +if ($env:PYTHON_EXECUTABLE -eq "python") { + $env:PIP_EXECUTABLE = "pip" +} +else { + $env:PIP_EXECUTABLE = "pip3" +} + +# Parse options +$EXECUTORCH_BUILD_PYBIND = "OFF" + +foreach ($arg in $args) { + switch ($arg) { + "--debug" { + $ENV:DEBUG = "1" + } + "--pybind" { + $EXECUTORCH_BUILD_PYBIND = "ON" + } + "coreml" { + if ($EXECUTORCH_BUILD_PYBIND -eq "ON") { + $arg_upper = $arg.ToUpper() + $CMAKE_ARGS = "$CMAKE_ARGS -DEXECUTORCH_BUILD_${arg_upper}=ON" + } + else { + Write-Error "Error: $arg must follow --pybind" + exit 1 + } + } + "mps" { + if ($EXECUTORCH_BUILD_PYBIND -eq "ON") { + $arg_upper = $arg.ToUpper() + $CMAKE_ARGS = "$CMAKE_ARGS -DEXECUTORCH_BUILD_${arg_upper}=ON" + } + else { + Write-Error "Error: $arg must follow --pybind" + exit 1 + } + } + "xnnpack" { + if ($EXECUTORCH_BUILD_PYBIND -eq "ON") { + $arg_upper = $arg.ToUpper() + $CMAKE_ARGS = "$CMAKE_ARGS -DEXECUTORCH_BUILD_${arg_upper}=ON" + } + else { + Write-Error "Error: $arg must follow --pybind" + exit 1 + } + } + default { + Write-Error "Error: Unknown option $arg" + exit 1 + } + } +} + +#TODO: support ninja +$CMAKE_ARGS = "$CMAKE_ARGS -T ClangCL" + +$NIGHTLY_VERSION = "dev20240716" + +# The pip repository that hosts nightly torch packages. +$TORCH_URL = "https://download.pytorch.org/whl/test/cpu" +$TORCH_NIGHTLY_URL="https://download.pytorch.org/whl/nightly/cpu" + +# pip packages needed by exir. +$EXIR_REQUIREMENTS = @( + "torch==2.5.0.${NIGHTLY_VERSION}" + "torchvision==0.20.0.${NIGHTLY_VERSION}" # For testing. +) + +# pip packages needed for development. +$DEVEL_REQUIREMENTS = @( + "cmake" # For building binary targets. + "pip>=23" # For building the pip package. + "pyyaml" # Imported by the kernel codegen tools. + "setuptools>=63" # For building the pip package. + "tomli" # Imported by extract_sources.py when using python < 3.11. + "wheel" # For building the pip package archive. + "zstd" # Imported by resolve_buck.py. +) + +# pip packages needed to run examples. +# TODO(dbort): Make each example publish its own requirements.txt +$EXAMPLES_REQUIREMENTS = @( + "timm==1.0.7" + "torchaudio==2.4.0.${NIGHTLY_VERSION}" + "torchsr==1.0.4" + "transformers==4.42.4" +) + +# Assemble the list of requirements to actually install. +# TODO(dbort): Add options for reducing the number of requirements. +$REQUIREMENTS_TO_INSTALL = @( + $EXIR_REQUIREMENTS + $DEVEL_REQUIREMENTS + $EXAMPLES_REQUIREMENTS +) + +# Install the requirements. `--extra-index-url` tells pip to look for package +# versions on the provided URL if they aren't available on the default URL. +& $ENV:PIP_EXECUTABLE install --extra-index-url $TORCH_NIGHTLY_URL $REQUIREMENTS_TO_INSTALL + +# +# Install executorch pip package. This also makes `flatc` available on the path. +# The --extra-index-url may be necessary if pyproject.toml has a dependency on a +# pre-release or nightly version of a torch package. +# +$ENV:EXECUTORCH_BUILD_PYBIND = $EXECUTORCH_BUILD_PYBIND +$ENV:CMAKE_ARGS = $CMAKE_ARGS +$ENV:CMAKE_BUILD_ARGS = $CMAKE_BUILD_ARGS +& $ENV:PIP_EXECUTABLE install . --no-build-isolation -v --extra-index-url $TORCH_NIGHTLY_URL From d43591b5ef2ef9659ab686e1b16a6fb6731f32ff Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Tue, 13 Aug 2024 22:16:04 -0400 Subject: [PATCH 3/4] Move ssize_t to compiler.h. --- extension/data_loader/file_data_loader.cpp | 5 ----- runtime/platform/compiler.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extension/data_loader/file_data_loader.cpp b/extension/data_loader/file_data_loader.cpp index 5881d3fc19e..3ece678084f 100644 --- a/extension/data_loader/file_data_loader.cpp +++ b/extension/data_loader/file_data_loader.cpp @@ -23,11 +23,6 @@ #include #endif -#ifdef _WIN32 -#include -using ssize_t = ptrdiff_t; -#endif - #include #include #include diff --git a/runtime/platform/compiler.h b/runtime/platform/compiler.h index c3907724130..5973735061f 100644 --- a/runtime/platform/compiler.h +++ b/runtime/platform/compiler.h @@ -133,6 +133,11 @@ #endif #endif // ifndef +#ifdef _WIN32 +#include +using ssize_t = ptrdiff_t; +#endif + // DEPRECATED: Use the non-underscore-prefixed versions instead. // TODO(T199005537): Remove these once all users have stopped using them. #define __ET_DEPRECATED ET_DEPRECATED From 1b8899c8b8251e0238262687eae913d47fedc49f Mon Sep 17 00:00:00 2001 From: Xiang Li Date: Tue, 20 Aug 2024 22:11:25 -0400 Subject: [PATCH 4/4] Update per comment. Switch to bat for Windows. --- extension/data_loader/file_data_loader.cpp | 8 +- extension/llm/custom_ops/CMakeLists.txt | 2 + install_requirements.bat | 21 ++++ install_requirements.ps1 | 125 ------------------- install_requirements.py | 4 + runtime/core/exec_aten/util/tensor_util.h | 5 - runtime/core/portable_type/tensor_impl.h | 7 -- runtime/platform/compiler.h | 4 +- setup.py | 134 +++++++++++++-------- 9 files changed, 116 insertions(+), 194 deletions(-) create mode 100644 install_requirements.bat delete mode 100644 install_requirements.ps1 diff --git a/extension/data_loader/file_data_loader.cpp b/extension/data_loader/file_data_loader.cpp index 3ece678084f..f84001d9be7 100644 --- a/extension/data_loader/file_data_loader.cpp +++ b/extension/data_loader/file_data_loader.cpp @@ -59,12 +59,10 @@ FileDataLoader::~FileDataLoader() { // file_name_ can be nullptr if this instance was moved from, but freeing a // null pointer is safe. std::free(const_cast(file_name_)); - // fd_ can be -1 if this instance was moved from, but closing a negative fd is - // safe (though it will return an error). -#ifdef _WIN32 - if (fd_ == -1) + // fd_ can be -1 if this instance was moved from. + if (fd_ == -1) { return; -#endif + } ::close(fd_); } diff --git a/extension/llm/custom_ops/CMakeLists.txt b/extension/llm/custom_ops/CMakeLists.txt index 32c10a17d1c..f057825ec80 100644 --- a/extension/llm/custom_ops/CMakeLists.txt +++ b/extension/llm/custom_ops/CMakeLists.txt @@ -102,6 +102,8 @@ if(EXECUTORCH_BUILD_KERNELS_CUSTOM_AOT) target_link_libraries(custom_ops_aot_lib PUBLIC cpublas torch) if(WIN32) + # There is no direct replacement for libpthread.so on Windows. + # For the Windows build, link directly against pthreadpool and cpuinfo. target_link_libraries(custom_ops_aot_lib PUBLIC pthreadpool cpuinfo) endif() target_compile_options( diff --git a/install_requirements.bat b/install_requirements.bat new file mode 100644 index 00000000000..4cfe4b21c4b --- /dev/null +++ b/install_requirements.bat @@ -0,0 +1,21 @@ +@ECHO OFF + +rem Copyright (c) Meta Platforms, Inc. and affiliates. +rem All rights reserved. + +rem This batch file provides a basic functionality similar to the bash script. + +cd /d "%~dp0" + +rem Find the names of the python tools to use (replace with your actual python installation) +if "%PYTHON_EXECUTABLE%"=="" ( + if "%CONDA_DEFAULT_ENV%"=="" OR "%CONDA_DEFAULT_ENV%"=="base" OR NOT EXIST "python" ( + set PYTHON_EXECUTABLE=python3 + ) else ( + set PYTHON_EXECUTABLE=python + ) +) + +"%PYTHON_EXECUTABLE%" install_requirements.py %* + +exit /b %ERRORLEVEL% \ No newline at end of file diff --git a/install_requirements.ps1 b/install_requirements.ps1 deleted file mode 100644 index 102121b17ac..00000000000 --- a/install_requirements.ps1 +++ /dev/null @@ -1,125 +0,0 @@ -# Check if the PYTHON_EXECUTABLE environment variable is set -if ([string]::IsNullOrEmpty($env:PYTHON_EXECUTABLE)) { - # Check if the CONDA_DEFAULT_ENV environment variable is empty, equal to "base", or if the "python" command is not executable - if ([string]::IsNullOrEmpty($env:CONDA_DEFAULT_ENV) -or $env:CONDA_DEFAULT_ENV -eq "base" -or !(Get-Command "python" -ErrorAction SilentlyContinue)) { - # Set PYTHON_EXECUTABLE to "python3" - $env:PYTHON_EXECUTABLE = "python3" - } - else { - # Set PYTHON_EXECUTABLE to "python" - $env:PYTHON_EXECUTABLE = "python" - } -} - -# Set the PIP_EXECUTABLE based on the value of PYTHON_EXECUTABLE -if ($env:PYTHON_EXECUTABLE -eq "python") { - $env:PIP_EXECUTABLE = "pip" -} -else { - $env:PIP_EXECUTABLE = "pip3" -} - -# Parse options -$EXECUTORCH_BUILD_PYBIND = "OFF" - -foreach ($arg in $args) { - switch ($arg) { - "--debug" { - $ENV:DEBUG = "1" - } - "--pybind" { - $EXECUTORCH_BUILD_PYBIND = "ON" - } - "coreml" { - if ($EXECUTORCH_BUILD_PYBIND -eq "ON") { - $arg_upper = $arg.ToUpper() - $CMAKE_ARGS = "$CMAKE_ARGS -DEXECUTORCH_BUILD_${arg_upper}=ON" - } - else { - Write-Error "Error: $arg must follow --pybind" - exit 1 - } - } - "mps" { - if ($EXECUTORCH_BUILD_PYBIND -eq "ON") { - $arg_upper = $arg.ToUpper() - $CMAKE_ARGS = "$CMAKE_ARGS -DEXECUTORCH_BUILD_${arg_upper}=ON" - } - else { - Write-Error "Error: $arg must follow --pybind" - exit 1 - } - } - "xnnpack" { - if ($EXECUTORCH_BUILD_PYBIND -eq "ON") { - $arg_upper = $arg.ToUpper() - $CMAKE_ARGS = "$CMAKE_ARGS -DEXECUTORCH_BUILD_${arg_upper}=ON" - } - else { - Write-Error "Error: $arg must follow --pybind" - exit 1 - } - } - default { - Write-Error "Error: Unknown option $arg" - exit 1 - } - } -} - -#TODO: support ninja -$CMAKE_ARGS = "$CMAKE_ARGS -T ClangCL" - -$NIGHTLY_VERSION = "dev20240716" - -# The pip repository that hosts nightly torch packages. -$TORCH_URL = "https://download.pytorch.org/whl/test/cpu" -$TORCH_NIGHTLY_URL="https://download.pytorch.org/whl/nightly/cpu" - -# pip packages needed by exir. -$EXIR_REQUIREMENTS = @( - "torch==2.5.0.${NIGHTLY_VERSION}" - "torchvision==0.20.0.${NIGHTLY_VERSION}" # For testing. -) - -# pip packages needed for development. -$DEVEL_REQUIREMENTS = @( - "cmake" # For building binary targets. - "pip>=23" # For building the pip package. - "pyyaml" # Imported by the kernel codegen tools. - "setuptools>=63" # For building the pip package. - "tomli" # Imported by extract_sources.py when using python < 3.11. - "wheel" # For building the pip package archive. - "zstd" # Imported by resolve_buck.py. -) - -# pip packages needed to run examples. -# TODO(dbort): Make each example publish its own requirements.txt -$EXAMPLES_REQUIREMENTS = @( - "timm==1.0.7" - "torchaudio==2.4.0.${NIGHTLY_VERSION}" - "torchsr==1.0.4" - "transformers==4.42.4" -) - -# Assemble the list of requirements to actually install. -# TODO(dbort): Add options for reducing the number of requirements. -$REQUIREMENTS_TO_INSTALL = @( - $EXIR_REQUIREMENTS - $DEVEL_REQUIREMENTS - $EXAMPLES_REQUIREMENTS -) - -# Install the requirements. `--extra-index-url` tells pip to look for package -# versions on the provided URL if they aren't available on the default URL. -& $ENV:PIP_EXECUTABLE install --extra-index-url $TORCH_NIGHTLY_URL $REQUIREMENTS_TO_INSTALL - -# -# Install executorch pip package. This also makes `flatc` available on the path. -# The --extra-index-url may be necessary if pyproject.toml has a dependency on a -# pre-release or nightly version of a torch package. -# -$ENV:EXECUTORCH_BUILD_PYBIND = $EXECUTORCH_BUILD_PYBIND -$ENV:CMAKE_ARGS = $CMAKE_ARGS -$ENV:CMAKE_BUILD_ARGS = $CMAKE_BUILD_ARGS -& $ENV:PIP_EXECUTABLE install . --no-build-isolation -v --extra-index-url $TORCH_NIGHTLY_URL diff --git a/install_requirements.py b/install_requirements.py index 5c9ecc182f6..4492a692ba3 100644 --- a/install_requirements.py +++ b/install_requirements.py @@ -83,6 +83,10 @@ def python_is_compatible(): print(f"Error: Unknown option {arg}") sys.exit(1) +# Use ClangCL on Windows. +if os.name == "nt": + CMAKE_ARGS += " -T ClangCL" + # Since ExecuTorch often uses main-branch features of pytorch, only the nightly # pip versions will have the required features. # diff --git a/runtime/core/exec_aten/util/tensor_util.h b/runtime/core/exec_aten/util/tensor_util.h index 198b92d5d82..b18cd349a62 100644 --- a/runtime/core/exec_aten/util/tensor_util.h +++ b/runtime/core/exec_aten/util/tensor_util.h @@ -15,11 +15,6 @@ #include // size_t #include -#ifdef _WIN32 -#include -using ssize_t = ptrdiff_t; -#endif - #include #include #include diff --git a/runtime/core/portable_type/tensor_impl.h b/runtime/core/portable_type/tensor_impl.h index c2f6ccb83a2..19977b71e09 100644 --- a/runtime/core/portable_type/tensor_impl.h +++ b/runtime/core/portable_type/tensor_impl.h @@ -8,13 +8,6 @@ #pragma once -#ifndef _WIN32 -#include // TODO(T126923429): Include size_t, ssize_t -#else -#include -using ssize_t = ptrdiff_t; -#endif - #include #include #include diff --git a/runtime/platform/compiler.h b/runtime/platform/compiler.h index 5973735061f..52a6deecdba 100644 --- a/runtime/platform/compiler.h +++ b/runtime/platform/compiler.h @@ -133,7 +133,9 @@ #endif #endif // ifndef -#ifdef _WIN32 +#ifndef _WIN32 +#include // TODO(T126923429): Include size_t, ssize_t +#else #include using ssize_t = ptrdiff_t; #endif diff --git a/setup.py b/setup.py index 5eab8167a3c..43d4a25eecd 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ import contextlib import os +import platform import re import sys @@ -162,6 +163,31 @@ def write_to_python_file(cls, path: str) -> None: fp.write("\n".join(lines) + "\n") +# The build type is determined by the DEBUG environment variable. If DEBUG is +# set to a non-empty value, the build type is Debug. Otherwise, the build type +# is Release. +def get_build_type(is_debug=None) -> str: + debug = int(os.environ.get("DEBUG", 0)) if is_debug is None else is_debug + cfg = "Debug" if debug else "Release" + return cfg + + +def get_dynamic_lib_name(name: str) -> str: + if platform.system() == "Windows": + return name + ".dll" + elif platform.system() == "Darwin": + return "lib" + name + ".dylib" + else: + return "lib" + name + ".so" + + +def get_executable_name(name: str) -> str: + if platform.system() == "Windows": + return name + ".exe" + else: + return name + + class _BaseExtension(Extension): """A base class that maps an abstract source to an abstract destination.""" @@ -189,10 +215,18 @@ def src_path(self, installer: "InstallerBuildExt") -> Path: installer: The InstallerBuildExt instance that is installing the file. """ - # share the cmake-out location with CustomBuild. - # Get a handle with installer.get_finalized_command('build') + # Share the cmake-out location with CustomBuild. cmake_cache_dir = Path(installer.get_finalized_command("build").cmake_cache_dir) + cfg = get_build_type(installer.debug) + + if os.name == "nt": + # Replace %BUILD_TYPE% with the current build type. + self.src = self.src.replace("%BUILD_TYPE%", cfg) + else: + # Remove %BUILD_TYPE% from the path. + self.src = self.src.replace("/%BUILD_TYPE%", "") + # Construct the full source path, resolving globs. If there are no glob # pattern characters, this will just ensure that the source file exists. srcs = tuple(cmake_cache_dir.glob(self.src)) @@ -212,17 +246,35 @@ class BuiltFile(_BaseExtension): `ext_modules`. """ - def __init__(self, src: str, dst: str): + def __init__( + self, + src_path: str, + src_name: str, + dst: str, + is_executable: bool = False, + is_dynamic_lib: bool = False, + ): """Initializes a BuiltFile. Args: - src: The path to the file to install, relative to the cmake-out - directory. May be an fnmatch-style glob that matches exactly one - file. + src_path: The path to the file to install without name, relative to the cmake-out + directory. + src_name: The name of the file to install dst: The path to install to, relative to the root of the pip package. If dst ends in "/", it is treated as a directory. Otherwise it is treated as a filename. + is_executable: If True, the file is an executable. This is used to + determine the destination filename for executable. + is_dynamic_lib: If True, the file is a dynamic library. This is used + to determine the destination filename for dynamic library. """ + if is_executable and is_dynamic_lib: + raise ValueError("is_executable and is_dynamic_lib cannot be both True.") + if is_executable: + src_name = get_executable_name(src_name) + elif is_dynamic_lib: + src_name = get_dynamic_lib_name(src_name) + src = os.path.join(src_path, src_name) # This is not a real extension, so use a unique name that doesn't look # like a module path. Some of setuptools's autodiscovery will look for # extension names with prefixes that match certain module paths. @@ -432,8 +484,7 @@ def initialize_options(self): def run(self): self.dump_options() - debug = int(os.environ.get("DEBUG", 0)) if self.debug is None else self.debug - cfg = "Debug" if debug else "Release" + cfg = get_build_type(self.debug) # get_python_lib() typically returns the path to site-packages, where # all pip packages in the environment are installed. @@ -508,8 +559,7 @@ def run(self): item for item in os.environ["CMAKE_BUILD_ARGS"].split(" ") if item ] - if os.name == "nt": - build_args += ["--config", cfg] + build_args += ["--config", cfg] # Put the cmake cache under the temp directory, like # "pip-out/temp./cmake-out". @@ -548,7 +598,7 @@ def run(self): "build/pip_data_bin_init.py.in", os.path.join(bin_dir, "__init__.py"), ) - # share the cmake-out location with _BaseExtension. + # Share the cmake-out location with _BaseExtension. self.cmake_cache_dir = cmake_cache_dir # Finally, run the underlying subcommands like build_py, build_ext. @@ -557,22 +607,16 @@ def run(self): def get_ext_modules() -> List[Extension]: """Returns the set of extension modules to build.""" - - debug = os.environ.get("DEBUG", 0) - cfg = "Debug" if debug else "Release" - ext_modules = [] if ShouldBuild.flatc(): - if os.name == "nt": - ext_modules.append( - BuiltFile( - f"third-party/flatbuffers/{cfg}/flatc.exe", "executorch/data/bin/" - ) - ) - else: - ext_modules.append( - BuiltFile("third-party/flatbuffers/flatc", "executorch/data/bin/") + ext_modules.append( + BuiltFile( + "third-party/flatbuffers/%BUILD_TYPE%/", + "flatc", + "executorch/data/bin/", + is_executable=True, ) + ) if ShouldBuild.pybindings(): ext_modules.append( @@ -584,35 +628,23 @@ def get_ext_modules() -> List[Extension]: ) ) if ShouldBuild.llama_custom_ops(): - if os.name == "nt": - ext_modules.append( - BuiltFile( - f"extension/llm/custom_ops/{cfg}/custom_ops_aot_lib.dll", - "executorch/extension/llm/custom_ops", - ) - ) - ext_modules.append( - # Install the prebuilt library for quantized ops required by custom ops. - BuiltFile( - f"kernels/quantized/{cfg}/quantized_ops_aot_lib.dll", - "executorch/kernels/quantized/", - ) - ) - else: - ext_modules.append( - # Install the prebuilt library for custom ops used in llama. - BuiltFile( - "extension/llm/custom_ops/libcustom_ops_aot_lib.*", - "executorch/extension/llm/custom_ops/", - ) + ext_modules.append( + BuiltFile( + "extension/llm/custom_ops/%BUILD_TYPE%/", + "custom_ops_aot_lib", + "executorch/extension/llm/custom_ops", + is_dynamic_lib=True, ) - ext_modules.append( - # Install the prebuilt library for quantized ops required by custom ops. - BuiltFile( - "kernels/quantized/libquantized_ops_aot_lib.*", - "executorch/kernels/quantized/", - ) + ) + ext_modules.append( + # Install the prebuilt library for quantized ops required by custom ops. + BuiltFile( + "kernels/quantized/%BUILD_TYPE%/", + "quantized_ops_aot_lib", + "executorch/kernels/quantized/", + is_dynamic_lib=True, ) + ) # Note that setuptools uses the presence of ext_modules as the main signal # that a wheel is platform-specific. If we install any platform-specific