diff --git a/.github/workflows/windows_wheel.yaml b/.github/workflows/windows_wheel.yaml new file mode 100644 index 000000000..709008bfe --- /dev/null +++ b/.github/workflows/windows_wheel.yaml @@ -0,0 +1,62 @@ +name: Build and test Windows wheel + +on: + pull_request: + push: + branches: + - nightly + - main + - release/* + tags: + - v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+ + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name }}-${{ github.ref_type == 'branch' && github.sha }}-${{ github.event_name == 'workflow_dispatch' }} + cancel-in-progress: true + +permissions: + id-token: write + contents: write + +defaults: + run: + shell: bash -l -eo pipefail {0} + +jobs: + + generate-matrix: + uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main + with: + package-type: wheel + os: windows + test-infra-repository: pytorch/test-infra + test-infra-ref: main + with-xpu: disable + with-rocm: disable + with-cuda: disable + build-python-only: "disable" + + build: + needs: generate-matrix + strategy: + fail-fast: false + name: Build and Upload Windows wheel + # TODO: use @main + uses: nicolashug/test-infra/.github/workflows/build_wheels_windows.yml@build-platform-windows + with: + repository: pytorch/torchcodec + ref: "" + test-infra-repository: nicolashug/test-infra + test-infra-ref: build-platform-windows + build-matrix: ${{ needs.generate-matrix.outputs.matrix }} + pre-script: packaging/pre_build_script.sh + # post-script: packaging/post_build_script.sh TODO: consider enabling post-build checks for Windows + env-script: packaging/vc_env_helper.bat + smoke-test-script: packaging/fake_smoke_test.py + package-name: torchcodec + trigger-event: ${{ github.event_name }} + build-platform: "python-build-package" + # The BUILD_AGAINST_ALL_FFMPEG_FROM_S3 var, needed to build the wheel, is + # set in vc_env_helper.bat Couldn't find a way to set it from here. + build-command: "python -m build --wheel -vvv --no-isolation" diff --git a/packaging/vc_env_helper.bat b/packaging/vc_env_helper.bat index cccbb4c2d..59a156402 100644 --- a/packaging/vc_env_helper.bat +++ b/packaging/vc_env_helper.bat @@ -24,6 +24,7 @@ if "%VSDEVCMD_ARGS%" == "" ( if "%CU_VERSION%" == "xpu" call "C:\Program Files (x86)\Intel\oneAPI\setvars.bat" set DISTUTILS_USE_SDK=1 +set BUILD_AGAINST_ALL_FFMPEG_FROM_S3=1 set args=%1 shift diff --git a/setup.py b/setup.py index 2efccf982..59b5ef53c 100644 --- a/setup.py +++ b/setup.py @@ -155,10 +155,10 @@ def copy_extensions_to_source(self): # dynamically loaded module. For more, see: # https://stackoverflow.com/a/2339910 extensions = ["dylib", "so"] + elif sys.platform in ("win32", "cygwin"): + extensions = ["dll"] else: - raise NotImplementedError( - "Platforms other than linux/darwin are not supported yet" - ) + raise NotImplementedError(f"Platform {sys.platform} is not supported") for ext in extensions: for lib_file in self._install_prefix.glob(f"*.{ext}"): diff --git a/src/torchcodec/_core/CMakeLists.txt b/src/torchcodec/_core/CMakeLists.txt index 9008aa909..1e6d2ec80 100644 --- a/src/torchcodec/_core/CMakeLists.txt +++ b/src/torchcodec/_core/CMakeLists.txt @@ -11,10 +11,29 @@ find_package(Python3 ${PYTHON_VERSION} EXACT COMPONENTS Development) if(DEFINED TORCHCODEC_DISABLE_COMPILE_WARNING_AS_ERROR AND TORCHCODEC_DISABLE_COMPILE_WARNING_AS_ERROR) set(TORCHCODEC_WERROR_OPTION "") else() - set(TORCHCODEC_WERROR_OPTION "-Werror") + if (WIN32) + # TODO set warnings as errors on Windows as well. + # set(TORCHCODEC_WERROR_OPTION "/WX") + else() + set(TORCHCODEC_WERROR_OPTION "-Werror") + endif() +endif() + +if (WIN32) + # Avoid warnings about non-ASCII characters in source files. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819") + # Important for when we add Windows CUDA: exporting all symbols is limited to + # 65535 symbols, which (apparently) will not work for CUDA. + # https://github.com/pytorch/pytorch/pull/3650 + set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) +endif() + +if (WIN32) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 ${TORCHCODEC_WERROR_OPTION} ${TORCH_CXX_FLAGS}") +else() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic ${TORCHCODEC_WERROR_OPTION} ${TORCH_CXX_FLAGS}") endif() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic ${TORCHCODEC_WERROR_OPTION} ${TORCH_CXX_FLAGS}") function(make_torchcodec_sublibrary library_name @@ -34,11 +53,26 @@ function(make_torchcodec_sublibrary # Avoid adding the "lib" prefix which we already add explicitly. set_target_properties(${library_name} PROPERTIES PREFIX "") + if(WIN32) + # On Windows, the built artifacts are put in Release/Debug + # subdirectories by default. We want to avoid that, otherwise our + # install() step would not know where to find those. + set_target_properties(${library_name} PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR} + LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} + LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR} + ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_BINARY_DIR} + ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_BINARY_DIR} + ) + endif() + target_link_libraries( ${library_name} PUBLIC ${library_dependencies} ) + endfunction() function(make_torchcodec_libraries @@ -144,11 +178,14 @@ function(make_torchcodec_libraries # stray initialization of py::objects. The rest of the object code must # match. See: # https://pybind11.readthedocs.io/en/stable/faq.html#someclass-declared-with-greater-visibility-than-the-type-of-its-field-someclass-member-wattributes - target_compile_options( - ${pybind_ops_library_name} - PUBLIC - "-fvisibility=hidden" - ) + if(NOT WIN32) + target_compile_options( + ${pybind_ops_library_name} + PUBLIC + "-fvisibility=hidden" + ) + endif() + # The value we use here must match the value we return from # _get_pybind_ops_module_name() on the Python side. If the values do not # match, then we will be unable to import the C++ shared library as a @@ -158,14 +195,17 @@ function(make_torchcodec_libraries PRIVATE PYBIND_OPS_MODULE_NAME=core_pybind_ops ) - # If we don't make sure this flag is set, we run into segfauls at import - # time on Mac. See: - # https://github.com/pybind/pybind11/issues/3907#issuecomment-1170412764 - target_link_options( - ${pybind_ops_library_name} - PUBLIC - "LINKER:-undefined,dynamic_lookup" - ) + + if(APPLE) + # If we don't make sure this flag is set, we run into segfauls at import + # time on Mac. See: + # https://github.com/pybind/pybind11/issues/3907#issuecomment-1170412764 + target_link_options( + ${pybind_ops_library_name} + PUBLIC + "LINKER:-undefined,dynamic_lookup" + ) + endif() # Install all libraries. set( @@ -183,7 +223,9 @@ function(make_torchcodec_libraries install( TARGETS ${all_libraries} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX} + RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX} # For Windows DLLs ) + endfunction() if(DEFINED ENV{BUILD_AGAINST_ALL_FFMPEG_FROM_S3}) diff --git a/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake b/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake index 35d3d06f8..22fbdff4c 100644 --- a/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake +++ b/src/torchcodec/_core/fetch_and_expose_non_gpl_ffmpeg_libs.cmake @@ -9,12 +9,20 @@ endif() include(FetchContent) +if (UNIX AND NOT APPLE) + set(LINUX TRUE) +else() + set(LINUX FALSE) +endif() + set( base_url https://pytorch.s3.amazonaws.com/torchcodec/ffmpeg/2025-03-14 ) -if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if (LINUX) + set(lib_dir "lib") + set( platform_url ${base_url}/linux_x86_64 @@ -36,7 +44,6 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") f7_sha256 1cb946d8b7c6393c2c3ebe1f900b8de7a2885fe614c45d4ec32c9833084f2f26 ) - set( f4_library_file_names libavutil.so.56 @@ -77,7 +84,8 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") libswscale.so.8 libswresample.so.5 ) -elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") +elseif (APPLE) + set(lib_dir "lib") set( platform_url ${base_url}/macos_arm64 @@ -98,6 +106,7 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") f7_sha256 48a4fc8ce098305cfd4a58f40889249c523ca3c285f66ba704b5bad0e3ada53a ) + set( f4_library_file_names libavutil.56.dylib @@ -138,6 +147,70 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin") libswscale.8.dylib libswresample.5.dylib ) + +elseif (WIN32) + set(lib_dir "bin") + set( + platform_url + ${base_url}/windows_x86_64 + ) + set( + f4_sha256 + 270a1aa8892225267e68a7eb87c417931da30dccbf08ee2bde8833e659cab5cb + ) + set( + f5_sha256 + b8b2a349a847e56a6da875b066dff1cae53cb8ee7cf5ba9321ec1243dea0cde0 + ) + set( + f6_sha256 + 5d9f8c76dc55f790fa31d825985e9270bf9e498b8bfec21a0ad3a1feb1fa053a + ) + set( + f7_sha256 + ae391ace382330e912793b70b68529ee7c91026d2869b4df7e7c3e7d3656bdd5 + ) + + set( + f4_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) + set( + f5_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) + set( + f6_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) + set( + f7_library_file_names + avutil.lib + avcodec.lib + avformat.lib + avdevice.lib + avfilter.lib + swscale.lib + swresample.lib + ) else() message( FATAL_ERROR @@ -183,24 +256,25 @@ target_include_directories(ffmpeg5 INTERFACE ${f5_SOURCE_DIR}/include) target_include_directories(ffmpeg6 INTERFACE ${f6_SOURCE_DIR}/include) target_include_directories(ffmpeg7 INTERFACE ${f7_SOURCE_DIR}/include) + list( TRANSFORM f4_library_file_names - PREPEND ${f4_SOURCE_DIR}/lib/ + PREPEND ${f4_SOURCE_DIR}/${lib_dir}/ OUTPUT_VARIABLE f4_library_paths ) list( TRANSFORM f5_library_file_names - PREPEND ${f5_SOURCE_DIR}/lib/ + PREPEND ${f5_SOURCE_DIR}/${lib_dir}/ OUTPUT_VARIABLE f5_library_paths ) list( TRANSFORM f6_library_file_names - PREPEND ${f6_SOURCE_DIR}/lib/ + PREPEND ${f6_SOURCE_DIR}/${lib_dir}/ OUTPUT_VARIABLE f6_library_paths ) list( TRANSFORM f7_library_file_names - PREPEND ${f7_SOURCE_DIR}/lib/ + PREPEND ${f7_SOURCE_DIR}/${lib_dir}/ OUTPUT_VARIABLE f7_library_paths ) diff --git a/src/torchcodec/_internally_replaced_utils.py b/src/torchcodec/_internally_replaced_utils.py index a8c84294c..e5b5847d1 100644 --- a/src/torchcodec/_internally_replaced_utils.py +++ b/src/torchcodec/_internally_replaced_utils.py @@ -18,10 +18,10 @@ def _get_extension_path(lib_name: str) -> str: extension_suffixes = importlib.machinery.EXTENSION_SUFFIXES elif sys.platform == "darwin": extension_suffixes = importlib.machinery.EXTENSION_SUFFIXES + [".dylib"] + elif sys.platform in ("win32", "cygwin"): + extension_suffixes = importlib.machinery.EXTENSION_SUFFIXES + [".dll"] else: - raise NotImplementedError( - "Platforms other than linux/darwin are not supported yet" - ) + raise NotImplementedError(f"{sys.platform = } is not not supported") loader_details = ( importlib.machinery.ExtensionFileLoader, extension_suffixes,