diff --git a/.github/actions/test-windows/action.yml b/.github/actions/test-windows/action.yml index 65d66ddb45..dab83ee9a3 100644 --- a/.github/actions/test-windows/action.yml +++ b/.github/actions/test-windows/action.yml @@ -9,5 +9,5 @@ runs: DEVICE: cpu run: | echo "::group::CPP tests - CPU" - ./build/tests/tests -tce="*gguf*,test random uniform" + ./build/tests.exe -tce="*gguf*,test random uniform" echo "::endgroup::" diff --git a/CMakeLists.txt b/CMakeLists.txt index 66731c4c1c..c2ecf411dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,6 @@ option(MLX_METAL_DEBUG "Enhance metal debug workflow" OFF) option(MLX_ENABLE_X64_MAC "Enable building for x64 macOS" OFF) option(MLX_BUILD_GGUF "Include support for GGUF format" ON) option(MLX_BUILD_SAFETENSORS "Include support for safetensors format" ON) -option(MLX_BUILD_BLAS_FROM_SOURCE "Build OpenBLAS from source code" OFF) option(MLX_BUILD_PYTHON_STUBS "Build stub files for python bindings" ON) option(MLX_METAL_JIT "Use JIT compilation for Metal kernels" OFF) option(MLX_USE_CCACHE "Use CCache for compilation cache when available" ON) @@ -219,14 +218,17 @@ if(WIN32) if(MSVC) # GGUF does not build with MSVC. set(MLX_BUILD_GGUF OFF) - # There is no prebuilt OpenBLAS distribution for MSVC. - set(MLX_BUILD_BLAS_FROM_SOURCE ON) + endif() + # Generate DLL and EXE in the same dir, otherwise EXE will not be able to run. + # This is only done when MLX is built as the top project. + if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) endif() # Windows implementation of dlfcn.h APIs. FetchContent_Declare( dlfcn-win32 GIT_REPOSITORY https://github.com/dlfcn-win32/dlfcn-win32.git - GIT_TAG v1.4.1 + GIT_TAG v1.4.2 EXCLUDE_FROM_ALL) block() set(BUILD_SHARED_LIBS OFF) @@ -250,23 +252,25 @@ if(MLX_BUILD_CPU) target_link_libraries(mlx PUBLIC ${ACCELERATE_LIBRARY}) add_compile_definitions(MLX_USE_ACCELERATE) add_compile_definitions(ACCELERATE_NEW_LAPACK) - elseif(MLX_BUILD_BLAS_FROM_SOURCE) - # Download and build OpenBLAS from source code. + elseif(WIN32) + # Download and link prebuilt binaries of OpenBLAS. Note that we can only + # link with the dynamic library, the prebuilt binaries were built with MinGW + # so static-linking would require linking with MinGW's runtime. FetchContent_Declare( openblas - GIT_REPOSITORY https://github.com/OpenMathLib/OpenBLAS.git - GIT_TAG v0.3.28 - EXCLUDE_FROM_ALL) - block(PROPAGATE openblas_SOURCE_DIR) - set(BUILD_SHARED_LIBS OFF) # link statically - set(BUILD_STATIC_LIBS ON) - set(NOFORTRAN ON) # msvc has no fortran compiler + URL "https://github.com/OpenMathLib/OpenBLAS/releases/download/v0.3.31/OpenBLAS-0.3.31-x64.zip" + ) FetchContent_MakeAvailable(openblas) - endblock() - target_link_libraries(mlx PRIVATE openblas) - target_include_directories( - mlx PRIVATE "${openblas_SOURCE_DIR}/lapack-netlib/LAPACKE/include" - "${CMAKE_BINARY_DIR}/generated" "${CMAKE_BINARY_DIR}") + target_link_libraries(mlx + PRIVATE "${openblas_SOURCE_DIR}/lib/libopenblas.lib") + target_include_directories(mlx PRIVATE "${openblas_SOURCE_DIR}/include") + # Make sure the DLL file is placed in the same dir with executables. + set(OPENBLAS_DLL_FILE "${openblas_SOURCE_DIR}/bin/libopenblas.dll") + add_custom_command( + TARGET mlx + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${OPENBLAS_DLL_FILE} + ${CMAKE_BINARY_DIR}) else() if(${CMAKE_HOST_APPLE}) # The blas shipped in macOS SDK is not supported, search homebrew for @@ -365,6 +369,15 @@ endif() # ----------------------------- Installation ----------------------------- include(GNUInstallDirs) +if(WIN32) + # Install DLLs to the same dir with extension file (core.pyd) on Windows. + set(CMAKE_INSTALL_BINDIR ".") + if(MLX_BUILD_CPU) + # Install OpenBLAS. + install(FILES ${OPENBLAS_DLL_FILE} TYPE BIN) + endif() +endif() + # Install library install( TARGETS mlx diff --git a/benchmarks/cpp/CMakeLists.txt b/benchmarks/cpp/CMakeLists.txt index 50b689dbff..82d5ffce9d 100644 --- a/benchmarks/cpp/CMakeLists.txt +++ b/benchmarks/cpp/CMakeLists.txt @@ -3,14 +3,6 @@ function(build_benchmark SRCFILE) set(target "${src_name}") add_executable(${target} ${SRCFILE}) target_link_libraries(${target} PRIVATE mlx) - # On Windows, copy the mlx DLL to the executable directory for runtime loading - if(WIN32 AND BUILD_SHARED_LIBS) - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ - $) - endif() endfunction(build_benchmark) build_benchmark(single_ops.cpp) diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index 433b849416..1b3969c97e 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -3,14 +3,6 @@ function(build_example SRCFILE) set(target "${src_name}") add_executable(${target} ${SRCFILE}) target_link_libraries(${target} PRIVATE mlx) - # On Windows, copy the mlx DLL to the executable directory for runtime loading - if(WIN32 AND BUILD_SHARED_LIBS) - add_custom_command( - TARGET ${target} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ - $) - endif() endfunction(build_example) build_example(tutorial.cpp) diff --git a/setup.py b/setup.py index 00c7514408..0176605a7b 100644 --- a/setup.py +++ b/setup.py @@ -95,6 +95,7 @@ def build_extension(self, ext: CMakeExtension) -> None: "-DMLX_BUILD_TESTS=OFF", "-DMLX_BUILD_BENCHMARKS=OFF", "-DMLX_BUILD_EXAMPLES=OFF", + "-DBUILD_SHARED_LIBS=ON", ] if build_stage == 2 and build_cuda: # Last arch is always real and virtual for forward-compatibility @@ -125,13 +126,6 @@ def build_extension(self, ext: CMakeExtension) -> None: archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) if archs: cmake_args += ["-DCMAKE_OSX_ARCHITECTURES={}".format(";".join(archs))] - if platform.system() == "Windows": - # On Windows DLLs must be put in the same dir with the extension - # while cmake puts mlx.dll into the "bin" sub-dir. Link with mlx - # statically to work around it. - cmake_args += ["-DBUILD_SHARED_LIBS=OFF"] - else: - cmake_args += ["-DBUILD_SHARED_LIBS=ON"] # Set CMAKE_BUILD_PARALLEL_LEVEL to control the parallel build level # across all generators. diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ed15546e2f..2a4a41c6b6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -40,15 +40,5 @@ target_link_libraries(tests PRIVATE mlx doctest) target_compile_options(tests PRIVATE ${SANITIZER_COMPILE_FLAGS}) target_link_options(tests PRIVATE ${SANITIZER_LINK_FLAGS}) -# On Windows, copy the mlx DLL to the test executable directory for runtime -# loading -if(WIN32 AND BUILD_SHARED_LIBS) - add_custom_command( - TARGET tests - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different $ - $) -endif() - doctest_discover_tests(tests) add_test(NAME tests COMMAND tests)