From 606f825eaad05dadc8c1c4ce81006f87089dc679 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Fri, 12 Jan 2024 15:24:09 -0800 Subject: [PATCH 1/2] Add patches for MakeMakefile Provides patches for MakeMakefile and modifies the Ruby path provided when building extensions to allow loading the custom mkmf.rb file. The patches perform inspection of flag values and quote any Windows paths found that are not already quoted. This resolves issues where builds fail due to spaces in compiler and linker flags on Windows. --- lib/vagrant/patches/builder/mkmf.rb | 116 ++++++++++++++++++++++++++++ lib/vagrant/patches/rubygems.rb | 23 ++++++ 2 files changed, 139 insertions(+) create mode 100644 lib/vagrant/patches/builder/mkmf.rb create mode 100644 lib/vagrant/patches/rubygems.rb diff --git a/lib/vagrant/patches/builder/mkmf.rb b/lib/vagrant/patches/builder/mkmf.rb new file mode 100644 index 00000000000..7c7d9d9c0b0 --- /dev/null +++ b/lib/vagrant/patches/builder/mkmf.rb @@ -0,0 +1,116 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +# This custom mkmf.rb file is used on Windows platforms +# to handle common path related build failures where +# a space is included in the path. The default installation +# location being in Program Files results in most many +# extensions failing to build. These patches will attempt +# to find unquoted paths in flags and quote them prior to +# usage. + +# Start with locating the real mkmf.rb file and +# loading it +mkmf_paths = $LOAD_PATH.find_all { |x| + !x.start_with?(__dir__) && + File.exist?(File.join(x, "mkmf.rb")) +}.uniq + +# At this point the path collection should only consist +# of a single entry. If there's more than one, load all +# of them but include a warning message that more than +# one was encountered. If none are found, then something +# bad is going on so just bail. +if mkmf_paths.size > 1 + $stderr.puts "WARNING: Multiple mkmf.rb files located: #{mkmf_paths.inspect}" +elsif mkmf_paths.empty? + raise "Failed to locate mkmf.rb file" +end + +mkmf_paths.each do |mpath| + require File.join(mpath, "mkmf.rb") +end + +# Attempt to detect and quote Windos paths found within +# the given string of flags +# +# @param [String] flags Compiler/linker flags +# @return [String] flags with paths quoted +def flag_cleaner(flags) + parts = flags.split(" -") + parts.map! do |p| + if p !~ %r{[A-Za-z]:(/|\\)} + next p + elsif p =~ %r{"[A-Za-z]:(/|\\).+"$} + next p + end + + p.gsub(%r{([A-Za-z]:(/|\\).+)$}, '"\1"') + end + + parts.join(" -") +end + +# Check values defined for CFLAGS, CPPFLAGS, LDFLAGS, +# and INCFLAGS for unquoted Windows paths and quote +# them. +def clean_flags! + $CFLAGS = flag_cleaner($CFLAGS) + $CPPFLAGS = flag_cleaner($CPPFLAGS) + $LDFLAGS = flag_cleaner($LDFLAGS) + $INCFLAGS = flag_cleaner($INCFLAGS) +end + +# Since mkmf loads the MakeMakefile module directly into the +# current scope, apply patches directly in the scope +def vagrant_create_makefile(*args) + clean_flags! + + ruby_create_makefile(*args) +end +alias :ruby_create_makefile :create_makefile +alias :create_makefile :vagrant_create_makefile + +def vagrant_append_cflags(*args) + result = ruby_append_cflags(*args) + clean_flags! + result +end +alias :ruby_append_cflags :append_cflags +alias :append_cflags :vagrant_append_cflags + +def vagrant_append_cppflags(*args) + result = ruby_append_cppflags(*args) + clean_flags! + result +end +alias :ruby_append_cppflags :append_cppflags +alias :append_cppflags :vagrant_append_cppflags + +def vagrant_append_ldflags(*args) + result = ruby_append_ldflags(*args) + clean_flags! + result +end +alias :ruby_append_ldflags :append_ldflags +alias :append_ldflags :vagrant_append_ldflags + +def vagrant_cc_config(*args) + clean_flags! + ruby_cc_config(*args) +end +alias :ruby_cc_config :cc_config +alias :cc_config :vagrant_cc_config + +def vagrant_link_config(*args) + clean_flags! + ruby_link_config(*args) +end +alias :ruby_link_config :link_config +alias :link_config :vagrant_link_config + +# Finally, always append the flags that Vagrant has +# defined via the environment +append_cflags(ENV["CFLAGS"]) if ENV["CFLAGS"] +append_cppflags(ENV["CPPFLAGS"]) if ENV["CPPFLAGS"] +append_ldflags(ENV["LDFLAGS"]) if ENV["LDFLAGS"] diff --git a/lib/vagrant/patches/rubygems.rb b/lib/vagrant/patches/rubygems.rb new file mode 100644 index 00000000000..3d844f0b53e --- /dev/null +++ b/lib/vagrant/patches/rubygems.rb @@ -0,0 +1,23 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +# This allows for effective monkey patching of the MakeMakefile +# module when building gem extensions. When gem extensions are +# built, the extconf.rb file is executed as a separate process. +# To support monkey patching the MakeMakefile module, the ruby +# executable path is adjusted to add a custom load path allowing +# a customized mkmf.rb file to load the proper mkmf.rb file, and +# then applying the proper patches. +if Gem.win_platform? + Gem.class_eval do + class << self + def vagrant_ruby + cmd = ruby_ruby + "#{cmd} -I\"#{Vagrant.source_root.join("lib/vagrant/patches/builder")}\"" + end + + alias_method :ruby_ruby, :ruby + alias_method :ruby, :vagrant_ruby + end + end +end From 886aab2ff9be858b58e91a8e43cdce2ef66d3b77 Mon Sep 17 00:00:00 2001 From: Chris Roberts Date: Fri, 12 Jan 2024 15:27:31 -0800 Subject: [PATCH 2/2] Load rubygem patches --- lib/vagrant.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/vagrant.rb b/lib/vagrant.rb index a346982a302..f790039d33b 100644 --- a/lib/vagrant.rb +++ b/lib/vagrant.rb @@ -1,11 +1,12 @@ # Copyright (c) HashiCorp, Inc. # SPDX-License-Identifier: BUSL-1.1 - require "log4r" # Add patches to log4r to support trace level require "vagrant/patches/log4r" require "vagrant/patches/net-ssh" +require "vagrant/patches/rubygems" + # Set our log levels and include trace require 'log4r/configurator' Log4r::Configurator.custom_levels(*(["TRACE"] + Log4r::Log4rConfig::LogLevels))