 configure.py                          |  6 +++--
 tools/gyp/pylib/gyp/generator/make.py | 41 +++++++++++++++++++++++++++++++++++
 tools/install.py                      |  5 ++++-
 3 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/configure.py b/configure.py
index c6e5154f..9757ded8 100755
--- a/configure.py
+++ b/configure.py
@@ -1633,6 +1633,8 @@ def configure_node(o):
     shlib_suffix = '%s.a'
   elif sys.platform.startswith('zos'):
     shlib_suffix = '%s.x'
+  elif sys.platform.startswith('win32') and 'GCC' in sys.version:
+    shlib_suffix = 'dll.a'
   else:
     shlib_suffix = 'so.%s'
   if '%s' in shlib_suffix:
@@ -1682,7 +1684,7 @@ def configure_library(lib, output, pkgname=None):
 
     # libpath needs to be provided ahead libraries
     if options.__dict__[shared_lib + '_libpath']:
-      if flavor == 'win':
+      if flavor == 'win' and 'MSC' in sys.version:
         if 'msvs_settings' not in output:
           output['msvs_settings'] = { 'VCLinkerTool': { 'AdditionalOptions': [] } }
         output['msvs_settings']['VCLinkerTool']['AdditionalOptions'] += [
@@ -2390,7 +2392,7 @@ gyp_args += ['-Dpython=' + python]
 
 if options.use_ninja:
   gyp_args += ['-f', 'ninja-' + flavor]
-elif flavor == 'win' and sys.platform != 'msys':
+elif flavor == 'win' and 'MSC' in sys.version:
   gyp_args += ['-f', 'msvs', '-G', 'msvs_version=auto']
 else:
   gyp_args += ['-f', 'make-' + flavor]
diff --git a/tools/gyp/pylib/gyp/generator/make.py b/tools/gyp/pylib/gyp/generator/make.py
index 7118492e..918e8a71 100644
--- a/tools/gyp/pylib/gyp/generator/make.py
+++ b/tools/gyp/pylib/gyp/generator/make.py
@@ -100,6 +100,8 @@ def CalculateVariables(default_variables, params):
         default_variables.setdefault("OS", operating_system)
         if flavor == "aix":
             default_variables.setdefault("SHARED_LIB_SUFFIX", ".a")
+        elif flavor == "win":
+            default_variables.setdefault("SHARED_LIB_SUFFIX", ".dll")
         elif flavor == "zos":
             default_variables.setdefault("SHARED_LIB_SUFFIX", ".x")
             COMPILABLE_EXTENSIONS.update({".pli": "pli"})
@@ -206,6 +208,26 @@ quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
 cmd_solink_module = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,-soname=$(@F) -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
 """  # noqa: E501
 
+LINK_COMMANDS_WINMINGW = """\
+quiet_cmd_alink = AR($(TOOLSET)) $@
+cmd_alink = rm -f $@ && echo $(filter %.o,$^) >> $(@).rsp && ar crs $@ @$(@).rsp
+
+quiet_cmd_alink_thin = AR($(TOOLSET)) $@
+cmd_alink_thin = rm -f $@ && echo $(filter %.o,$^) >> $(@).rsp && ar crsT $@ $(filter %.o,$^) @$(@).rsp
+
+# Due to circular dependencies between libraries :(, we wrap the
+# special "figure out circular dependencies" flags around the entire
+# input list during linking.
+quiet_cmd_link = LINK($(TOOLSET)) $@
+cmd_link = $(LINK.$(TOOLSET)) -o $@ $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,--start-group $(LD_INPUTS) $(LIBS) -Wl,--end-group
+
+quiet_cmd_solink = SOLINK($(TOOLSET)) $@
+cmd_solink = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,--out-implib,$(builddir)/$(@F).a -Wl,--whole-archive $(LD_INPUTS) -Wl,--no-whole-archive $(LIBS)
+
+quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@
+cmd_solink_module = $(LINK.$(TOOLSET)) -o $@ -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -Wl,--out-implib,$(builddir)/$(@F).a -Wl,--start-group $(filter-out FORCE_DO_CMD, $^) -Wl,--end-group $(LIBS)
+"""
+
 LINK_COMMANDS_MAC = """\
 quiet_cmd_alink = LIBTOOL-STATIC $@
 cmd_alink = rm -f $@ && %(python)s gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %%.o,$^)
@@ -380,6 +402,8 @@ CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS)
 LINK.target ?= %(LINK.target)s
 LDFLAGS.target ?= $(LDFLAGS)
 AR.target ?= %(AR.target)s
+RC.target ?= $(RC.target)s
+RCFLAGS.target ?= $(RCFLAGS)
 PLI.target ?= %(PLI.target)s
 
 # C++ apps need to be linked with g++.
@@ -394,6 +418,8 @@ CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host)
 LINK.host ?= %(LINK.host)s
 LDFLAGS.host ?= $(LDFLAGS_host)
 AR.host ?= %(AR.host)s
+RC.host ?= $(RC.host)s
+RCFLAGS.host ?= $(RCFLAGS_host)
 PLI.host ?= %(PLI.host)s
 
 # Define a dir function that can handle spaces.
@@ -478,6 +504,9 @@ cmd_cxx = $(CXX.$(TOOLSET)) -o $@ $< $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TO
 quiet_cmd_touch = TOUCH $@
 cmd_touch = touch $@
 
+quiet_cmd_rc = RC($(TOOLSET)) $@
+cmd_rc = windres -o $@ $< -O coff -Isrc $(RCFLAGS.$(TOOLSET))
+
 quiet_cmd_copy = COPY $@
 # send stderr to /dev/null to ignore messages when linking directories.
 cmd_copy = ln -f "$<" "$@" 2>/dev/null || (rm -rf "$@" && cp %(copy_archive_args)s "$<" "$@")
@@ -683,6 +712,7 @@ COMPILABLE_EXTENSIONS = {
     ".cc": "cxx",
     ".cpp": "cxx",
     ".cxx": "cxx",
+    ".rc": "rc",
     ".s": "cc",
     ".S": "cc",
 }
@@ -1584,6 +1614,8 @@ $(obj).$(TOOLSET)/$(TARGET)/%%.o: $(obj)/%%%s FORCE_DO_CMD
                 target_ext = ".a"
             elif self.flavor == "zos":
                 target_ext = ".x"
+            elif self.flavor == "win":
+                target_ext = ".dll"
             else:
                 target_ext = ".so"
         elif self.type == "none":
@@ -2556,6 +2588,15 @@ def GenerateOutput(target_list, target_dicts, data, params):
     elif flavor == "openbsd":
         copy_archive_arguments = "-pPRf"
         header_params.update({"copy_archive_args": copy_archive_arguments})
+    elif flavor == "win":
+        copy_archive_arguments = "-pPRf"
+        header_params.update(
+            {
+                "RC.target": GetEnvironFallback(("RC_target", "RC"), "windres"),
+                "RC.host": GetEnvironFallback(("RC_host", "RC"), "windres"),
+                "link_commands": LINK_COMMANDS_WINMINGW,
+            }
+        )
     elif flavor == "aix":
         copy_archive_arguments = "-pPRf"
         header_params.update(
diff --git a/tools/install.py b/tools/install.py
index 24c297d6..344fbd18 100755
--- a/tools/install.py
+++ b/tools/install.py
@@ -158,7 +158,10 @@ def files(options, action):
   if 'true' == options.variables.get('node_shared'):
     if options.is_win:
       action(options, [os.path.join(options.build_dir, 'libnode.dll')], 'bin/libnode.dll')
-      action(options, [os.path.join(options.build_dir, 'libnode.lib')], 'lib/libnode.lib')
+      if 'MSC' in sys.version:
+        action(options, [os.path.join(options.build_dir, 'libnode.lib')], 'lib/libnode.lib')
+      else:
+        action(options, [os.path.join(options.build_dir, 'libnode.dll.a')], 'lib/libnode.dll.a')
     elif sys.platform == 'zos':
       # GYP will output to lib.target; see _InstallableTargetInstallPath
       # function in tools/gyp/pylib/gyp/generator/make.py
