@@ -506,15 +506,30 @@ def run_cmake_configure(self, additional_args: Optional[List[str]] = None) -> No
506506 cmake_cmd .extend (additional_args )
507507
508508 # Add PyHelios-specific options
509- cmake_cmd .extend ([
510- '-DBUILD_SHARED_LIBS=ON' , # Build as shared library
511- ])
509+ if self .platform_name == 'Windows' :
510+ # On Windows, disable shared libs for dependencies but enable for PyHelios
511+ cmake_cmd .extend ([
512+ '-DBUILD_SHARED_LIBS=OFF' , # Static dependencies to avoid DLL hell
513+ '-DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded' , # Static MSVC runtime
514+ '-DZLIB_ROOT=' , # Use bundled zlib
515+ '-DPNG_BUILD_ZLIB=ON' , # Build PNG with bundled zlib
516+ '-DPNG_SHARED=OFF' , # Static libpng
517+ '-DJPEG_BUILD_SHARED=OFF' , # Static libjpeg
518+ ])
519+ else :
520+ cmake_cmd .extend ([
521+ '-DBUILD_SHARED_LIBS=ON' , # Build as shared library on Unix
522+ ])
512523
513524 # Windows-specific workaround for zlib resource compilation issue
514525 if self .platform_name == 'Windows' :
515526 # The RC compiler fails on win32/zlib1.rc due to C syntax in included headers
516527 # Patch zlib CMakeLists.txt to disable the problematic shared library target
517528 self ._patch_zlib_cmake_for_windows ()
529+
530+ # Disable resource compilation entirely if CMAKE_RC_COMPILER is not set
531+ if not os .environ .get ('CMAKE_RC_COMPILER' ):
532+ cmake_cmd .extend (['-DCMAKE_RC_COMPILER=' ]) # Empty RC compiler disables resource compilation
518533
519534 print (f"Running CMake configure: { ' ' .join (cmake_cmd )} " )
520535
@@ -589,6 +604,67 @@ def find_built_library(self) -> Path:
589604
590605 raise HeliosBuildError (f"Built library { lib_name } not found in build directory" )
591606
607+ def _check_windows_dll_dependencies (self , dll_path : Path ) -> Dict [str , bool ]:
608+ """
609+ Check Windows DLL dependencies using dumpbin if available.
610+
611+ Args:
612+ dll_path: Path to the DLL to check
613+
614+ Returns:
615+ Dictionary of dependency name -> found status
616+ """
617+ dependencies = {}
618+
619+ try :
620+ # Try using dumpbin to check dependencies
621+ result = subprocess .run (
622+ ['dumpbin' , '/dependents' , str (dll_path )],
623+ capture_output = True , text = True , timeout = 30
624+ )
625+
626+ if result .returncode == 0 :
627+ lines = result .stdout .split ('\n ' )
628+ in_dependencies = False
629+
630+ for line in lines :
631+ line = line .strip ()
632+ if 'Image has the following dependencies:' in line :
633+ in_dependencies = True
634+ continue
635+ elif in_dependencies and line :
636+ if line .endswith ('.dll' ):
637+ dep_name = line .lower ()
638+ # Check if this is a system DLL that should be available
639+ system_dlls = {
640+ 'kernel32.dll' , 'user32.dll' , 'gdi32.dll' , 'advapi32.dll' ,
641+ 'msvcrt.dll' , 'vcruntime140.dll' , 'msvcp140.dll' , 'ucrtbase.dll'
642+ }
643+
644+ if dep_name in system_dlls :
645+ dependencies [dep_name ] = True # Assume system DLLs are available
646+ else :
647+ # Check if the DLL exists in system paths
648+ try :
649+ result_check = subprocess .run (
650+ ['where' , dep_name ], capture_output = True
651+ )
652+ dependencies [dep_name ] = result_check .returncode == 0
653+ except :
654+ dependencies [dep_name ] = False
655+ elif line .startswith ('Summary' ):
656+ break
657+
658+ print (f"DLL dependencies for { dll_path .name } :" )
659+ for dep , found in dependencies .items ():
660+ status = "✓" if found else "✗"
661+ print (f" { status } { dep } " )
662+
663+ except (subprocess .TimeoutExpired , FileNotFoundError , subprocess .CalledProcessError ):
664+ print (f"Could not check DLL dependencies (dumpbin not available)" )
665+
666+ return dependencies
667+
592668 def _validate_library_loadable (self , library_path : Path ) -> None :
593669 """
594670 Validate that the library can be loaded by ctypes.
@@ -606,7 +682,16 @@ def _validate_library_loadable(self, library_path: Path) -> None:
606682 try :
607683 import ctypes
608684
685+ # Check dependencies before attempting to load (Windows only)
609686 if self .platform_name == 'Windows' :
687+ print (f"Checking Windows DLL dependencies for: { library_path .name } " )
688+ dependencies = self ._check_windows_dll_dependencies (library_path )
689+ missing_deps = [dep for dep , found in dependencies .items () if not found ]
690+
691+ if missing_deps :
692+ print (f"WARNING: Missing dependencies detected: { missing_deps } " )
693+ print ("This may cause ctypes loading to fail" )
694+
610695 # Test Windows DLL loading
611696 test_lib = ctypes .WinDLL (str (library_path ))
612697 else :
@@ -618,22 +703,42 @@ def _validate_library_loadable(self, library_path: Path) -> None:
618703
619704 except Exception as e :
620705 # FAIL-FAST: Library cannot be loaded by ctypes
621- error_msg = (
622- f"CRITICAL: Built library cannot be loaded by ctypes: { library_path } \n "
623- f"Load error: { e } \n \n "
624- f"This is a fatal build error. PyHelios requires libraries that can be loaded by ctypes.\n \n "
625- f"Possible causes:\n "
626- f"1. Static library (.a) was created instead of shared library (.dylib/.so/.dll)\n "
627- f"2. Missing dependencies or incorrect linking\n "
628- f"3. Architecture mismatch (x86 vs ARM64)\n "
629- f"4. Corrupted library file\n \n "
630- f"Solutions:\n "
631- f"1. Ensure CMake builds shared libraries: -DBUILD_SHARED_LIBS=ON\n "
632- f"2. Check that all dependencies are available\n "
633- f"3. Rebuild with correct architecture flags\n "
634- f"4. Verify library dependencies with 'otool -L' (macOS) or 'ldd' (Linux)\n \n "
635- f"This build cannot proceed with an unusable library."
636- )
706+ if self .platform_name == 'Windows' :
707+ # Windows-specific error handling and dependency checking
708+ error_msg = (
709+ f"CRITICAL: Built Windows DLL cannot be loaded by ctypes: { library_path } \n "
710+ f"Load error: { e } \n \n "
711+ f"This indicates missing DLL dependencies. Common causes:\n \n "
712+ f"1. Visual C++ Runtime Libraries missing\n "
713+ f"2. Static linking not configured properly\n "
714+ f"3. Third-party dependencies (zlib, libpng, etc.) not embedded\n "
715+ f"4. Architecture mismatch (x86 vs x64)\n \n "
716+ f"Windows-specific solutions:\n "
717+ f"1. Install Visual C++ Redistributable (vcredist)\n "
718+ f"2. Use static linking: -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded\n "
719+ f"3. Ensure -DBUILD_SHARED_LIBS=OFF for dependencies\n "
720+ f"4. Check DLL dependencies with 'dumpbin /dependents { library_path .name } '\n "
721+ f"5. Verify architecture matches Python (x86 vs x64)\n \n "
722+ f"The built DLL has missing dependencies and cannot be used."
723+ )
724+ else :
725+ # Unix error handling
726+ error_msg = (
727+ f"CRITICAL: Built library cannot be loaded by ctypes: { library_path } \n "
728+ f"Load error: { e } \n \n "
729+ f"This is a fatal build error. PyHelios requires libraries that can be loaded by ctypes.\n \n "
730+ f"Possible causes:\n "
731+ f"1. Static library (.a) was created instead of shared library (.dylib/.so/.dll)\n "
732+ f"2. Missing dependencies or incorrect linking\n "
733+ f"3. Architecture mismatch (x86 vs ARM64)\n "
734+ f"4. Corrupted library file\n \n "
735+ f"Solutions:\n "
736+ f"1. Ensure CMake builds shared libraries: -DBUILD_SHARED_LIBS=ON\n "
737+ f"2. Check that all dependencies are available\n "
738+ f"3. Rebuild with correct architecture flags\n "
739+ f"4. Verify library dependencies with 'otool -L' (macOS) or 'ldd' (Linux)\n \n "
740+ f"This build cannot proceed with an unusable library."
741+ )
637742 raise HeliosBuildError (error_msg )
638743
639744 def copy_to_output (self , library_path : Path ) -> Path :
@@ -1269,6 +1374,12 @@ def _patch_zlib_cmake_for_windows(self) -> None:
12691374 (" set_target_properties(zlib PROPERTIES SUFFIX \" 1.dll\" )" , " # set_target_properties(zlib PROPERTIES SUFFIX \" 1.dll\" )" ),
12701375 # Update install targets to exclude zlib shared library
12711376 (" install(TARGETS zlib zlibstatic" , " install(TARGETS zlibstatic" ),
1377+ # Add static runtime linking for zlib
1378+ ("add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})" ,
1379+ "add_library(zlibstatic STATIC ${ZLIB_SRCS} ${ZLIB_PUBLIC_HDRS} ${ZLIB_PRIVATE_HDRS})\n # PYHELIOS PATCH: Use static MSVC runtime\n if(MSVC)\n set_target_properties(zlibstatic PROPERTIES MSVC_RUNTIME_LIBRARY \" MultiThreaded$<$<CONFIG:Debug>:Debug>\" )\n endif()" ),
1380+ # Disable problematic resource compilation
1381+ ("if(NOT MINGW)\n set(ZLIB_DLL_SRCS\n win32/zlib1.rc # If present will override custom build rule below.\n )\n endif()" ,
1382+ "# PYHELIOS PATCH: Disable resource compilation to avoid RC errors\n # if(NOT MINGW)\n # set(ZLIB_DLL_SRCS\n # win32/zlib1.rc # If present will override custom build rule below.\n # )\n # endif()" ),
12721383 ]
12731384
12741385 for old , new in patches :
0 commit comments