From 3d09924d3b6c1bcdbb427d4c566bbdd155204ba3 Mon Sep 17 00:00:00 2001
From: Zhou Qiankang <wszqkzqk@qq.com>
Date: Sun, 23 Nov 2025 11:56:46 +0800
Subject: [PATCH] build: support to build pymol with mingw-w64 on Windows

Signed-off-by: Zhou Qiankang <wszqkzqk@qq.com>
---
 .../plugins/molfile_plugin/src/dtrplugin.hxx  |  2 +-
 .../uiuc/plugins/molfile_plugin/src/ply_c.h   |  2 +-
 layer0/strcasecmp.h                           |  2 +-
 ov/src/ov_types.h                             |  2 +-
 setup.py                                      | 81 +++++++++++++------
 5 files changed, 61 insertions(+), 28 deletions(-)

diff --git a/contrib/uiuc/plugins/molfile_plugin/src/dtrplugin.hxx b/contrib/uiuc/plugins/molfile_plugin/src/dtrplugin.hxx
index 87820cc19..f49123525 100644
--- a/contrib/uiuc/plugins/molfile_plugin/src/dtrplugin.hxx
+++ b/contrib/uiuc/plugins/molfile_plugin/src/dtrplugin.hxx
@@ -42,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #ifndef MOLFILE_DTRPLUGIN_HXX
 #define MOLFILE_DTRPLUGIN_HXX
 
-#if defined(_MSC_VER)
+#if defined(_WIN32)
 #ifndef DESRES_WIN32
 #define DESRES_WIN32
 #endif
diff --git a/contrib/uiuc/plugins/molfile_plugin/src/ply_c.h b/contrib/uiuc/plugins/molfile_plugin/src/ply_c.h
index 31a7d5f9f..1727aed51 100644
--- a/contrib/uiuc/plugins/molfile_plugin/src/ply_c.h
+++ b/contrib/uiuc/plugins/molfile_plugin/src/ply_c.h
@@ -3121,7 +3121,7 @@ void *get_new_props_ply(PlyFile *ply)
   }
 
   /* in case we need a random choice */
-#if defined(_MSC_VER)
+#if defined(_WIN32)
   random_pick = 0;
 #else
   random_pick = (int) floor (rules->nprops * drand48());
diff --git a/layer0/strcasecmp.h b/layer0/strcasecmp.h
index 1704e8200..d4f803f8b 100644
--- a/layer0/strcasecmp.h
+++ b/layer0/strcasecmp.h
@@ -4,7 +4,7 @@
 
 #pragma once
 
-#ifdef _WIN32
+#if defined(_WIN32) && defined(_MSC_VER)
   #include <string.h>
   #define strcasecmp(s1, s2) _stricmp(s1, s2)
   #define strncasecmp(s1, s2, n) _strnicmp(s1, s2, n)
diff --git a/ov/src/ov_types.h b/ov/src/ov_types.h
index 470733852..632f52877 100644
--- a/ov/src/ov_types.h
+++ b/ov/src/ov_types.h
@@ -60,7 +60,7 @@ extern "C" {
   typedef unsigned int ov_uint32;
   typedef float ov_float32;
   typedef double ov_float64;
-#ifdef WIN32
+#ifdef _MSC_VER
   typedef __int64 ov_int64;
   typedef unsigned __int64 ov_uint64;
 #else
diff --git a/setup.py b/setup.py
index ec7237a86..09d0743bf 100644
--- a/setup.py
+++ b/setup.py
@@ -31,6 +31,13 @@
 MAC = sys.platform.startswith("darwin")
 
 
+# check for mingw compiler on windows
+is_mingw = False
+if WIN:
+    from distutils import ccompiler
+    is_mingw = ccompiler.get_default_compiler() == 'mingw32'
+
+
 # Have to copy from "create_shadertext.py" script due to the use of pyproject.toml
 # Full explanation:
 # https://github.com/pypa/setuptools/issues/3939
@@ -269,6 +276,10 @@ def get_prefix_path() -> list[str]:
 
         paths += [sys.prefix] + paths
 
+    if WIN and is_mingw:
+        if os.path.isdir(sys.prefix):
+            paths.append(os.path.normpath(sys.prefix))
+
     paths += ["/usr"]
 
     return paths
@@ -566,8 +577,9 @@ def make_launch_script(self):
 
 libs = ["png", "freetype"]
 lib_dirs = []
-ext_comp_args = (
-    [
+ext_comp_args = []
+if is_mingw or not WIN:
+    ext_comp_args.extend([
         "-Werror=return-type",
         "-Wunused-variable",
         "-Wno-switch",
@@ -576,27 +588,27 @@ def make_launch_script(self):
         "-Wno-char-subscripts",
         # optimizations
         "-Og" if DEBUG else "-O3",
-    ]
-    if not WIN
-    else ["/MP"]
-)
+    ])
+else:  # MSVC
+    ext_comp_args.extend([
+        "/MP",
+        "/std:c++17",
+    ])
 ext_link_args = []
 ext_objects = []
 data_files = []
 ext_modules = []
 
 if options.use_openmp == "yes":
-    def_macros += [
-        ("PYMOL_OPENMP", None),
-    ]
-    if MAC:
-        ext_comp_args += ["-Xpreprocessor", "-fopenmp"]
-        libs += ["omp"]
-    elif WIN:
-        ext_comp_args += ["/openmp"]
-    else:
-        ext_comp_args += ["-fopenmp"]
-        ext_link_args += ["-fopenmp"]
+    def_macros += [("PYMOL_OPENMP", None)]
+    if WIN and not is_mingw:  # MSVC
+        ext_comp_args.append("/openmp")
+    elif MAC:  # macOS Clang
+        ext_comp_args.extend(["-Xpreprocessor", "-fopenmp"])
+        libs.append("omp")
+    else:  # GCC/Clang on Linux, and MinGW on Windows
+        ext_comp_args.append("-fopenmp")
+        ext_link_args.append("-fopenmp")
 
 if options.vmd_plugins:
     # VMD plugin support
@@ -701,14 +713,18 @@ def make_launch_script(self):
     )
 
     if DEBUG:
-        ext_comp_args += ["/Z7"]
-        ext_link_args += ["/DEBUG"]
+        if is_mingw:
+            ext_comp_args += ["-g"]
+        else:
+            ext_comp_args += ["/Z7"]
+            ext_link_args += ["/DEBUG"]
 
     libs += [
         "opengl32",
     ]
-    # TODO: Remove when we move to setup-CMake
-    ext_comp_args += ["/std:c++17"]
+    if not is_mingw:
+        # TODO: Remove when we move to setup-CMake
+        ext_comp_args += ["/std:c++17"]
 
 if not (MAC or WIN):
     libs += [
@@ -814,10 +830,26 @@ def get_packages(base, parent="", r=None):
 champ_inc_dirs.append(sysconfig.get_paths()["include"])
 champ_inc_dirs.append(sysconfig.get_paths()["platinclude"])
 
+champ_libs = []
+
 if WIN:
-    # pyconfig.py forces linking against pythonXY.lib on MSVC
-    py_lib = pathlib.Path(sysconfig.get_paths()["stdlib"]).parent / "libs"
-    lib_dirs.append(str(py_lib))
+    if not is_mingw:
+        # pyconfig.py forces linking against pythonXY.lib on MSVC
+        py_lib = pathlib.Path(sysconfig.get_paths()["stdlib"]).parent / "libs"
+        lib_dirs.append(str(py_lib))
+    else:
+        ldversion = (
+            sysconfig.get_config_var("LDVERSION")
+            or f"{sys.version_info.major}.{sys.version_info.minor}"
+        )
+        libs.append(f"python{ldversion}")
+        champ_libs.append(f"python{ldversion}")
+        mingw_libdir = (
+            sysconfig.get_config_var("LIBDIR")
+            or os.path.join(sys.prefix, "lib")
+        )
+        if mingw_libdir and os.path.isdir(mingw_libdir):
+            lib_dirs.append(mingw_libdir)
 
 ext_modules += [
     CMakeExtension(
@@ -834,6 +866,7 @@ def get_packages(base, parent="", r=None):
         name="chempy.champ._champ",
         sources=get_sources(["contrib/champ"]),
         include_dirs=champ_inc_dirs,
+        libraries=champ_libs,
         library_dirs=lib_dirs,
     ),
 ]
