summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChris Reuter <[email protected]>2021-10-21 21:33:17 -0400
committerChris Reuter <[email protected]>2021-10-21 21:58:45 -0400
commit5c4273f944b538bc24ed98c52991ea8bf9044654 (patch)
tree2d65523aa60dff53a4cedd95e92abf25b0755a39
parentb6b4ac8270fcef135291cbde60d18f1c8a4c98e4 (diff)
downloadmruby-5c4273f944b538bc24ed98c52991ea8bf9044654.tar.gz
mruby-5c4273f944b538bc24ed98c52991ea8bf9044654.zip
Added testing support for cross-MinGW builds.
This adds a build_config that will cross-build a Windows executable using the MinGW cross-compiler and will also run the unit (i.e. 'rake test') using Wine. For this to work, I made some modifications to the underlying test scripts as well as some minor changes to a couple of the tests themselves.
-rw-r--r--build_config/cross-mingw-winetest.rb91
-rwxr-xr-xbuild_config/helpers/wine_runner.rb71
-rw-r--r--lib/mruby/build.rb17
-rw-r--r--lib/mruby/build/command.rb5
-rw-r--r--mrbgems/mruby-bin-mrbc/bintest/mrbc.rb4
-rw-r--r--mrbgems/mruby-bin-mruby/bintest/mruby.rb4
-rw-r--r--test/bintest.rb18
7 files changed, 204 insertions, 6 deletions
diff --git a/build_config/cross-mingw-winetest.rb b/build_config/cross-mingw-winetest.rb
new file mode 100644
index 000000000..fad06b265
--- /dev/null
+++ b/build_config/cross-mingw-winetest.rb
@@ -0,0 +1,91 @@
+
+# Cross-compile using MinGW and test using Wine.
+#
+# Steps:
+#
+# 1. Install MinGW; 64-bit target seems to work best.
+#
+# 2. Install Wine.
+#
+# 3. Run command:
+#
+# wine cmd /c echo "Hello world"'
+#
+# This will confirm that Wine works and will trigger standard
+# Wine setup, which is slow.
+#
+# 4. Confirm that drive 'z:' is mapped to your root filesystem.
+# (This is supposed to be a default but it helps to
+# double-check.) To confirm, run:
+#
+# wine cmd /c dir 'z:\\'
+#
+# This should give you a DOS-style equivalent of 'ls /'. If not,
+# you'll need to fix that with winecfg or by adding a symlink to
+# '~/.wine/dosdevices'.
+#
+# 5. You will likely need to tweak the settings below to work with
+# your configuration unless it is exactly like one of the platforms
+# I've tested on (Ubuntu 20.04 or macOS using brew.)
+#
+# 6. Run the build command:
+#
+# MRUBY_CONFIG=build_config/cross-mingw-winetest.rb rake test
+#
+# If all goes well, you should now have Windows executables and a
+# set of passing tests.
+#
+#
+# Caveats:
+#
+# 1. This works by using a helper script that rewrites test output
+# to make it look *nix-like and then handing it back to the test
+# cases. Some of the existing tests were (slightly) modified to
+# make this easier but only for the 'full-core' gembox. Other
+# gems' bintests may or may not work with the helper script and
+# may or may not be fixable by extending the script.
+#
+# 2. MinGW and Wine are both complex and not very consistent so you
+# will likely need to do some fiddling to get things to work.
+#
+# 3. This script assumes you are running it on a *nix-style OS.
+#
+# 4. I recommend building 64-bit targets only. Building a 32-bit
+# Windows binary with i686-w64-mingw32 seems to work (at least,
+# it did for me) but the resulting executable failed a number of
+# unit tests due to small errors in some floating point
+# operations. It's unclear if this indicates more serious problems.
+#
+
+
+MRuby::CrossBuild.new("cross-mingw-winetest") do |conf|
+ conf.toolchain :gcc
+
+ conf.host_target = "x86_64-w64-mingw32"
+
+ # Ubuntu 20
+ conf.cc.command = "#{conf.host_target}-gcc-posix"
+
+ # macOS+Wine from brew
+ #conf.cc.command = "#{conf.host_target}-gcc"
+
+ conf.linker.command = conf.cc.command
+ conf.archiver.command = "#{conf.host_target}-gcc-ar"
+ conf.exts.executable = ".exe"
+
+ # By default, we compile as static as possible to remove runtime
+ # MinGW dependencies; they are probably fixable but it gets
+ # complicated.
+ conf.cc.flags = ['-static']
+ conf.linker.flags += ['-static']
+
+ conf.test_runner do |t|
+ thisdir = File.absolute_path( File.dirname(__FILE__) )
+ t.command = File.join(thisdir, * %w{ helpers wine_runner.rb})
+ end
+
+ conf.gembox "full-core"
+
+ conf.enable_bintest
+ conf.enable_test
+end
diff --git a/build_config/helpers/wine_runner.rb b/build_config/helpers/wine_runner.rb
new file mode 100755
index 000000000..9a7eb46b0
--- /dev/null
+++ b/build_config/helpers/wine_runner.rb
@@ -0,0 +1,71 @@
+#!/usr/bin/env ruby
+
+# Wrapper for running tests for cross-compiled Windows builds in Wine.
+
+require 'open3'
+
+DOSROOT = 'z:'
+
+# Rewrite test output to replace DOS-isms with Unix-isms.
+def clean(output, stderr = false)
+ ends_with_newline = !!(output =~ /\n$/)
+ executable = ARGV[0].gsub(/\.exe\z/i, '')
+
+ # Fix line-ends
+ output = output.gsub(/\r\n/, "\n")
+
+ # Strip out Wine messages
+
+
+ results = output.split(/\n/).map do |line|
+ # Fix file paths
+ if line =~ /#{DOSROOT}\\/i
+ line.gsub!(/#{DOSROOT}([^:]*)/i) { |path|
+ path.gsub!(/^#{DOSROOT}/i, '')
+ path.gsub!(%r{\\}, '/')
+ path
+ }
+ end
+
+ # strip '.exe' off the end of the executable's name if needed
+ line.gsub!(/(#{Regexp.escape executable})\.exe/i, '\1')
+
+ line
+ end
+
+ result_text = results.join("\n")
+ result_text += "\n" if ends_with_newline
+ return result_text
+end
+
+
+def main
+ if ARGV.empty? || ARGV[0] =~ /^- (-?) (\?|help|h) $/x
+ puts "#{$0} <command-line>"
+ exit 0
+ end
+
+ # For simplicity, just read all of stdin into memory and pass that
+ # as an argument when invoking wine. (Skipped if STDIN was not
+ # redirected.)
+ if !STDIN.tty?
+ input = STDIN.read
+ else
+ input = ""
+ end
+
+ # Disable all Wine messages so they don't interfere with the output
+ ENV['WINEDEBUG'] = 'err-all,warn-all,fixme-all,trace-all'
+
+ # Run the program in wine and capture the output
+ output, errormsg, status = Open3.capture3('wine', *ARGV, :stdin_data => input)
+
+ # Clean and print the results.
+ STDOUT.write clean(output)
+ STDERR.write clean(errormsg)
+
+ exit(status.exitstatus)
+end
+
+
+main()
diff --git a/lib/mruby/build.rb b/lib/mruby/build.rb
index 67267ab46..66005a6df 100644
--- a/lib/mruby/build.rb
+++ b/lib/mruby/build.rb
@@ -541,6 +541,23 @@ EOS
end
end
+ def run_bintest
+ puts ">>> Bintest #{name} <<<"
+ targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir }
+ targets << filename(".") if File.directory? "./bintest"
+ mrbc = @gems["mruby-bin-mrbc"] ? exefile("#{@build_dir}/bin/mrbc") : mrbcfile
+
+ emulator = @test_runner.command
+ emulator = @test_runner.shellquote(emulator) if emulator
+
+ env = {
+ "BUILD_DIR" => @build_dir,
+ "MRBCFILE" => mrbc,
+ "EMULATOR" => @test_runner.emulator,
+ }
+ sh env, "ruby test/bintest.rb#{verbose_flag} #{targets.join ' '}"
+ end
+
protected
def create_mrbc_build; end
diff --git a/lib/mruby/build/command.rb b/lib/mruby/build/command.rb
index c93d08ea7..31a595ef0 100644
--- a/lib/mruby/build/command.rb
+++ b/lib/mruby/build/command.rb
@@ -363,6 +363,11 @@ module MRuby
@flags = []
end
+ def emulator
+ return "" unless @command
+ return [@command, *@flags].map{|c| shellquote(c)}.join(' ')
+ end
+
def run(testbinfile)
puts "TEST for " + @build.name
_run runner_options, { :flags => [flags, verbose_flag].flatten.join(' '), :infile => testbinfile }
diff --git a/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb b/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb
index f4d9208b3..90bbd123f 100644
--- a/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb
+++ b/mrbgems/mruby-bin-mrbc/bintest/mrbc.rb
@@ -7,7 +7,7 @@ assert('Compiling multiple files without new line in last line. #2361') do
b.write('module B; end')
b.flush
result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} #{b.path} 2>&1`
- assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
+ assert_equal "#{cmd_bin('mrbc')}:#{a.path}:Syntax OK", result.chomp
assert_equal 0, $?.exitstatus
end
@@ -16,7 +16,7 @@ assert('parsing function with void argument') do
a.write('f ()')
a.flush
result = `#{cmd('mrbc')} -c -o #{out.path} #{a.path} 2>&1`
- assert_equal "#{cmd('mrbc')}:#{a.path}:Syntax OK", result.chomp
+ assert_equal "#{cmd_bin('mrbc')}:#{a.path}:Syntax OK", result.chomp
assert_equal 0, $?.exitstatus
end
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
index bc25f18c1..a626a13cd 100644
--- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb
+++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb
@@ -2,7 +2,7 @@ require 'tempfile'
require 'open3'
def assert_mruby(exp_out, exp_err, exp_success, args)
- out, err, stat = Open3.capture3(cmd("mruby"), *args)
+ out, err, stat = Open3.capture3( *(cmd_list("mruby") + args))
assert "assert_mruby" do
assert_operator(exp_out, :===, out, "standard output")
assert_operator(exp_err, :===, err, "standard error")
@@ -87,7 +87,7 @@ assert('mruby -e option (no code specified)') do
end
assert('mruby -h option') do
- assert_mruby(/\AUsage: #{Regexp.escape cmd("mruby")} .*/m, "", true, %w[-h])
+ assert_mruby(/\AUsage: #{Regexp.escape cmd_bin("mruby")} .*/m, "", true, %w[-h])
end
assert('mruby -r option') do
diff --git a/test/bintest.rb b/test/bintest.rb
index a6888c9fb..773d61edc 100644
--- a/test/bintest.rb
+++ b/test/bintest.rb
@@ -3,13 +3,27 @@ require 'test/assert.rb'
GEMNAME = ""
-def cmd(s)
+def cmd_list(s)
path = s == "mrbc" ? ENV['MRBCFILE'] : "#{ENV['BUILD_DIR']}/bin/#{s}"
path = path.sub(/\.exe\z/, "")
if /mswin(?!ce)|mingw|bccwin/ =~ RbConfig::CONFIG['host_os']
path = "#{path}.exe".tr("/", "\\")
end
- path
+
+ path_list = [path]
+
+ emu = ENV['EMULATOR']
+ path_list.unshift emu if emu && !emu.empty?
+
+ path_list
+end
+
+def cmd(s)
+ return cmd_list(s).join(' ')
+end
+
+def cmd_bin(s)
+ return cmd_list(s).pop
end
def shellquote(s)