summaryrefslogtreecommitdiff
path: root/spec/ruby/optional/capi/spec_helper.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/ruby/optional/capi/spec_helper.rb')
-rw-r--r--spec/ruby/optional/capi/spec_helper.rb172
1 files changed, 172 insertions, 0 deletions
diff --git a/spec/ruby/optional/capi/spec_helper.rb b/spec/ruby/optional/capi/spec_helper.rb
new file mode 100644
index 0000000000..49ce23d874
--- /dev/null
+++ b/spec/ruby/optional/capi/spec_helper.rb
@@ -0,0 +1,172 @@
+# Require the main spec_helper.rb at the end to let `ruby ...spec.rb` work
+
+# MRI magic to use built but not installed ruby
+$extmk = false
+
+require 'rbconfig'
+
+OBJDIR ||= File.expand_path("../../../ext/#{RUBY_ENGINE}/#{RUBY_VERSION}", __FILE__)
+
+def object_path
+ path = OBJDIR
+ if ENV['SPEC_CAPI_CXX'] == 'true'
+ path = "#{path}/cxx"
+ end
+ mkdir_p(path)
+ path
+end
+
+def compile_extension(name)
+ debug = false
+ cxx = ENV['SPEC_CAPI_CXX'] == 'true'
+ run_mkmf_in_process = RUBY_ENGINE == 'truffleruby'
+
+ core_ext_dir = File.expand_path("../ext", __FILE__)
+
+ spec_caller_location = caller_locations.find { |c| c.path.end_with?('_spec.rb') }
+ spec_file_path = spec_caller_location.path
+ spec_ext_dir = File.expand_path("../ext", spec_file_path)
+
+ ext = "#{name}_spec"
+ lib = "#{object_path}/#{ext}.#{RbConfig::CONFIG['DLEXT']}"
+ rubyhdrdir = RbConfig::CONFIG['rubyhdrdir']
+ ruby_header = "#{rubyhdrdir}/ruby.h"
+ abi_header = "#{rubyhdrdir}/ruby/internal/abi.h"
+
+ if RbConfig::CONFIG["ENABLE_SHARED"] == "yes"
+ # below is defined since 2.1, except for mswin, and maybe other platforms
+ libdirname = RbConfig::CONFIG.fetch 'libdirname', 'libdir'
+ libruby = "#{RbConfig::CONFIG[libdirname]}/#{RbConfig::CONFIG['LIBRUBY']}"
+ end
+
+ begin
+ mtime = File.mtime(lib)
+ rescue Errno::ENOENT
+ # not found, then compile
+ else
+ case # if lib is older than headers, source or libruby, then recompile
+ when mtime <= File.mtime("#{core_ext_dir}/rubyspec.h")
+ when mtime <= File.mtime("#{spec_ext_dir}/#{ext}.c")
+ when mtime <= File.mtime(ruby_header)
+ when (mtime <= File.mtime(abi_header) rescue nil)
+ when libruby && mtime <= File.mtime(libruby)
+ else
+ return lib # up-to-date
+ end
+ end
+
+ # Copy needed source files to tmpdir
+ tmpdir = tmp("cext_#{name}")
+ Dir.mkdir(tmpdir)
+ begin
+ files = ["#{core_ext_dir}/rubyspec.h", "#{spec_ext_dir}/#{ext}.c"]
+ if spec_ext_dir != core_ext_dir
+ files += Dir.glob("#{spec_ext_dir}/*.h")
+ end
+ files.each do |file|
+ if cxx and file.end_with?('.c')
+ cp file, "#{tmpdir}/#{File.basename(file, '.c')}.cpp"
+ else
+ cp file, "#{tmpdir}/#{File.basename(file)}"
+ end
+ end
+
+ Dir.chdir(tmpdir) do
+ if run_mkmf_in_process
+ required = require 'mkmf'
+ # Reinitialize mkmf if already required
+ init_mkmf unless required
+ create_makefile(ext, tmpdir)
+ else
+ # Workaround for digest C-API specs to find the ruby/digest.h header
+ # when run in the CRuby repository via make test-spec
+ if MSpecScript.instance_variable_defined?(:@testing_ruby)
+ ruby_repository_extra_include_dir = "-I#{RbConfig::CONFIG.fetch("prefix")}/#{RbConfig::CONFIG.fetch("EXTOUT")}/include"
+ end
+
+ File.write("extconf.rb", <<-RUBY)
+ require 'mkmf'
+ $ruby = ENV.values_at('RUBY_EXE', 'RUBY_FLAGS').join(' ')
+ # MRI magic to consider building non-bundled extensions
+ $extout = nil
+ if RbConfig::CONFIG.key?("buildlibdir") # The top directory where the libruby is built.
+ # Prepend the dummy macro to bypass the conversion in `with_destdir` on DOSISH
+ # platforms, where the drive letter is replaced with `$(DESTDIR)`. `DESTDIR` is
+ # overridden by the command line argument bellow.
+ RbConfig::MAKEFILE_CONFIG["buildlibdir"] = "$(empty)" + RbConfig::CONFIG["buildlibdir"]
+ end
+ append_cflags '-Wno-declaration-after-statement'
+ #{"append_cflags #{ruby_repository_extra_include_dir.inspect}" if ruby_repository_extra_include_dir}
+ create_makefile(#{ext.inspect})
+ RUBY
+ output = ruby_exe("extconf.rb")
+ raise "extconf failed:\n#{output}" unless $?.success?
+ $stderr.puts output if debug
+ end
+
+ # Do not capture stderr as we want to show compiler warnings
+ make, opts = setup_make
+ output = IO.popen([*make, "V=1", "DESTDIR=", opts], &:read)
+ raise "#{make} failed:\n#{output}" unless $?.success?
+ $stderr.puts output if debug
+
+ cp File.basename(lib), lib
+ end
+ ensure
+ rm_r tmpdir
+ end
+
+ File.chmod(0755, lib)
+ lib
+end
+
+def setup_make
+ make = ENV['MAKE']
+ make ||= (RbConfig::CONFIG['host_os'].include?("mswin") ? "nmake" : "make")
+ env = %w[MFLAGS MAKEFLAGS GNUMAKEFLAGS].to_h {|var| [var, ENV[var]]}
+ make_flags = env["MAKEFLAGS"] || ''
+
+ # suppress logo of nmake.exe to stderr
+ if File.basename(make, ".*").downcase == "nmake" and !make_flags.include?("l")
+ env["MAKEFLAGS"] = "l#{make_flags}"
+ end
+
+ opts = {}
+ if /(?:\A|\s)--jobserver-(?:auth|fds)=(\d+),(\d+)/ =~ make_flags
+ [$1, $2].each do |fd|
+ fd = IO.for_fd(fd.to_i(10), autoclose: false)
+ opts[fd] = fd
+ rescue
+ # Jobserver is not usable, maybe no `+` flag or on Windows.
+ job_options = /\A\s*(?:-j\d+\s*|-\S+\Kj\d+)|(?:\G|\s)\K--jobserver-(?:auth|fds)=\S+\s*/
+ env["MAKEFLAGS"] = make_flags.gsub(job_options, '')
+ %w[GNUMAKEFLAGS MFLAGS].each do |var|
+ if flags = env[var]
+ env[var] = flags.gsub(job_options, '')
+ end
+ end
+ opts.clear
+ break
+ end
+ end
+
+ [[env, make], opts]
+end
+
+def load_extension(name)
+ ext_path = compile_extension(name)
+ require ext_path
+ ext_path
+rescue LoadError => e
+ if %r{/usr/sbin/execerror ruby "\(ld 3 1 main ([/a-zA-Z0-9_\-.]+_spec\.so)"} =~ e.message
+ system('/usr/sbin/execerror', "#{RbConfig::CONFIG["bindir"]}/ruby", "(ld 3 1 main #{$1}")
+ end
+ raise
+end
+
+# Constants
+CAPI_SIZEOF_LONG = [0].pack('l!').size
+
+# Require the main spec_helper.rb only here so load_extension() is defined
+# when running specs with `ruby ...spec.rb`
+require_relative '../../spec_helper'