Hi, I tried to build swig-rb on Windows, however it is unable to build with msvc and svn/util.rb doesn't work with x64-mswin64. I created a patch for the issues. See the attached patch, swig-rb-x64-mswin64.patch.txt.
Also, if you want to build Subversion with msvc, you need Ruby for mswin. However, RubyInstaller, which is often used on Windows, is built with msys2 toolset, which is not compatible with msvc. So I built Ruby 3.1.4, 3.2.5. 3.3.5 and 3.4.0-preview2 with msvc and published at [1]. Otherwise, you could use daily build for ruby master branch is published at [2]. I've confirmed that the tests for swig-rb pass on Windows [3], could someone please test it? [1] https://github.com/jun66j5/ruby-build/releases/tag/v20241013 [2] https://github.com/MSP-Greg/ruby-loco/releases/tag/ruby-master [3] https://github.com/jun66j5/subversion-build/actions/runs/11640686568 -- Jun Omae <jun6...@gmail.com> (大前 潤)
From dbe067bbc2f37c50435b76e09cf53a11cfabdc4e Mon Sep 17 00:00:00 2001 From: Jun Omae <jun6...@gmail.com> Date: Sun, 13 Oct 2024 08:42:03 +0900 Subject: [PATCH] swig-rb: Support ruby x64-mswin64. * build/generator/gen_win_dependencies.py (_find_ruby): Adapt the match for mswin version from ruby/setup-ruby, `x.y.z+n`. * subversion/bindings/swig/include/svn_types.swg (%typemap(in) const char *header_encoding): Use `NUM2SWIG` rather than `NUM2INT` to match the size of a pointer. * subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c (rb_set_pool_callback): Added in order to able to pass it to `rb_iterate` without the cast to a function pointer. (svn_swig_rb_set_baton): Declare it using `RB_BLOCK_CALL_FUNC_ARGLIST` for the block function. (rb_set_pool_for_hash_callback, svn_swig_rb_to_apr_array_row_prop_callback, svn_swig_rb_to_apr_array_prop_callback, r2c_hash_i, callback, callback_rescue, callback_handle_error): Change the types of the parameters in order to able to pass without the cast to a function pointer. (svn_swig_rb_set_pool, svn_swig_rb_set_pool_for_no_swig_type, svn_swig_rb_to_apr_array_row_prop, svn_swig_rb_to_apr_array_prop, r2c_hash, invoke_callback, callback_handle_error, invoke_callback_handle_error, svn_swig_rb_set_baton): Remove the unnecessary casts to a function pointer. * subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.h (ssize_t): Define `ssize_t` only if it is not defined. Also, use `apr_ssize_t` to define it rather than `long` in order to make compatible on both LP64 and LLP64 environments. (#pragma C4113): Configure C4113 as warning instead of error if SWIG is 4.0.2 early. * subversion/bindings/swig/ruby/svn/util.rb (): Adapt the regular expression to x64-mswin64. (reset_message_directory, windows?): Ditto. * subversion/bindings/swig/ruby/test/test_fs.rb (test_transaction, test_prop): Add sleep of 32ms if Windows because resolution of the Subversion's clock on Windows is 16ms. * subversion/bindings/swig/ruby/test/test_repos.rb (assert_transaction): Ditto. * subversion/bindings/swig/ruby/test/windows_util.rb (setup_svnserve): Fix launch of the svnserve process on Windows. (teardown_svnserve): Implement stop of the svnserve process on Windows. * subversion/bindings/swig/svn_delta.i (svn_swig_rb_delta_editor_get_target_revision): Use `NUM2SWIG` rather than `NUM2LONG` to match the size of a pointer on both LP64 and LLP64 environments. * subversion/bindings/swig/svn_wc.i (%typemap(argout) svn_revnum_t *target_revision): Use `SWIG2NUM` rather than `LONG2NUM` to match the size of a pointer on both LP64 and LLP64 environments. * win-tests.py (Run run-test.rb): Pass `--collector=dir` option to `run-test.rb` to run the tests well. --- build/generator/gen_win_dependencies.py | 2 +- .../bindings/swig/include/svn_types.swg | 2 +- .../swig/ruby/libsvn_swig_ruby/swigutil_rb.c | 58 ++++++++++--------- .../libsvn_swig_ruby/swigutil_rb__pre_ruby.h | 8 ++- subversion/bindings/swig/ruby/svn/util.rb | 6 +- subversion/bindings/swig/ruby/test/test_fs.rb | 12 ++-- .../bindings/swig/ruby/test/test_repos.rb | 1 + .../bindings/swig/ruby/test/windows_util.rb | 27 ++++----- subversion/bindings/swig/svn_delta.i | 2 +- subversion/bindings/swig/svn_wc.i | 2 +- win-tests.py | 2 +- 11 files changed, 64 insertions(+), 58 deletions(-) diff --git a/build/generator/gen_win_dependencies.py b/build/generator/gen_win_dependencies.py index 5444f1223bea4..e8c0625edbf95 100644 --- a/build/generator/gen_win_dependencies.py +++ b/build/generator/gen_win_dependencies.py @@ -1029,7 +1029,7 @@ def _find_ruby(self, show_warnings): else: defines = [] - ver = ruby_version.split('.') + ver = ruby_version.split('+')[0].split('.') ver = tuple(map(int, ver)) if ver >= (1, 8, 0): defines.extend(["HAVE_RB_ERRINFO"]) diff --git a/subversion/bindings/swig/include/svn_types.swg b/subversion/bindings/swig/include/svn_types.swg index 07d4c85dec683..06005153acc83 100644 --- a/subversion/bindings/swig/include/svn_types.swg +++ b/subversion/bindings/swig/include/svn_types.swg @@ -394,7 +394,7 @@ svn_ ## TYPE ## _swig_rb_closed(VALUE self) if (NIL_P($input)) { } else if (TYPE($input) == T_FIXNUM) { - $1 = (char *)NUM2INT($input); + $1 = (char *)NUM2SWIG($input); if (!($1 == APR_LOCALE_CHARSET || $1 == APR_DEFAULT_CHARSET)) { $1 = NULL; } diff --git a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c index 439d05abb714d..58145da3a685a 100644 --- a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c +++ b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb.c @@ -695,6 +695,12 @@ rb_set_pool(VALUE self, VALUE pool) return Qnil; } +static VALUE +rb_set_pool_callback(RB_BLOCK_CALL_FUNC_ARGLIST(self, pool)) +{ + return rb_set_pool(self, pool); +} + static VALUE rb_pool_new(VALUE parent) { @@ -760,9 +766,10 @@ struct rb_set_pool_for_hash_arg { }; static int -rb_set_pool_for_hash_callback(VALUE key, VALUE value, - struct rb_set_pool_for_hash_arg *arg) +rb_set_pool_for_hash_callback(VALUE key, VALUE value, VALUE hash_arg) { + struct rb_set_pool_for_hash_arg *arg; + arg = (struct rb_set_pool_for_hash_arg *) hash_arg; if (svn_swig_rb_set_pool(value, arg->pool)) arg->set = TRUE; return ST_CONTINUE; @@ -788,7 +795,7 @@ svn_swig_rb_set_pool(VALUE target, VALUE pool) struct rb_set_pool_for_hash_arg arg; arg.set = FALSE; arg.pool = pool; - rb_hash_foreach(target, (int(*)(ANYARGS))rb_set_pool_for_hash_callback, (VALUE)&arg); + rb_hash_foreach(target, rb_set_pool_for_hash_callback, (VALUE)&arg); return arg.set; } else { return rb_set_pool_if_swig_type_object(target, pool); @@ -806,7 +813,7 @@ svn_swig_rb_set_pool_for_no_swig_type(VALUE target, VALUE pool) target = rb_ary_new3(1, target); } - rb_iterate((VALUE(*)())rb_each, target, (VALUE(*)())rb_set_pool, pool); + rb_iterate(rb_each, target, rb_set_pool_callback, pool); } void @@ -1028,9 +1035,10 @@ typedef struct prop_hash_each_arg_t { } prop_hash_each_arg_t; static int -svn_swig_rb_to_apr_array_row_prop_callback(VALUE key, VALUE value, - prop_hash_each_arg_t *arg) +svn_swig_rb_to_apr_array_row_prop_callback(VALUE key, VALUE value, VALUE + prop_arg) { + prop_hash_each_arg_t *arg = (prop_hash_each_arg_t *) prop_arg; svn_prop_t *prop; prop = apr_array_push(arg->array); @@ -1071,7 +1079,7 @@ svn_swig_rb_to_apr_array_row_prop(VALUE array_or_hash, apr_pool_t *pool) arg.array = result; arg.pool = pool; rb_hash_foreach(array_or_hash, - (int(*)(ANYARGS))svn_swig_rb_to_apr_array_row_prop_callback, + svn_swig_rb_to_apr_array_row_prop_callback, (VALUE)&arg); return result; } else { @@ -1082,9 +1090,9 @@ svn_swig_rb_to_apr_array_row_prop(VALUE array_or_hash, apr_pool_t *pool) } static int -svn_swig_rb_to_apr_array_prop_callback(VALUE key, VALUE value, - prop_hash_each_arg_t *arg) +svn_swig_rb_to_apr_array_prop_callback(VALUE key, VALUE value, VALUE prop_arg) { + prop_hash_each_arg_t *arg = (prop_hash_each_arg_t *) prop_arg; svn_prop_t *prop; prop = apr_palloc(arg->pool, sizeof(svn_prop_t)); @@ -1127,7 +1135,7 @@ svn_swig_rb_to_apr_array_prop(VALUE array_or_hash, apr_pool_t *pool) arg.array = result; arg.pool = pool; rb_hash_foreach(array_or_hash, - (int(*)(ANYARGS))svn_swig_rb_to_apr_array_prop_callback, + svn_swig_rb_to_apr_array_prop_callback, (VALUE)&arg); return result; } else { @@ -1525,8 +1533,9 @@ svn_swig_rb_apr_revnum_key_hash_to_hash_string(apr_hash_t *hash) /* Ruby Hash -> apr_hash_t */ static int -r2c_hash_i(VALUE key, VALUE value, hash_to_apr_hash_data_t *data) +r2c_hash_i(VALUE key, VALUE value, VALUE data_arg) { + hash_to_apr_hash_data_t *data = (hash_to_apr_hash_data_t *) data_arg; if (key != Qundef) { void *val = data->func(value, data->ctx, data->pool); svn_hash_sets(data->apr_hash, apr_pstrdup(data->pool, StringValuePtr(key)), @@ -1550,7 +1559,7 @@ r2c_hash(VALUE hash, r2c_func func, void *ctx, apr_pool_t *pool) data.func = func; data.pool = pool; - rb_hash_foreach(hash, (int(*)(ANYARGS))r2c_hash_i, (VALUE)&data); + rb_hash_foreach(hash, r2c_hash_i, (VALUE)&data); return apr_hash; } @@ -1609,7 +1618,7 @@ typedef struct callback_handle_error_baton_t { } callback_handle_error_baton_t; static VALUE -callback(VALUE baton, ...) +callback(VALUE baton) { callback_baton_t *cbb = (callback_baton_t *)baton; VALUE result; @@ -1621,7 +1630,7 @@ callback(VALUE baton, ...) } static VALUE -callback_rescue(VALUE baton, ...) +callback_rescue(VALUE baton, VALUE unused) { callback_rescue_baton_t *rescue_baton = (callback_rescue_baton_t*)baton; @@ -1638,7 +1647,7 @@ callback_rescue(VALUE baton, ...) } static VALUE -callback_ensure(VALUE pool, ...) +callback_ensure(VALUE pool) { svn_swig_rb_pop_pool(pool); @@ -1655,19 +1664,18 @@ invoke_callback(VALUE baton, VALUE pool) argv[0] = pool; svn_swig_rb_get_pool(1, argv, Qnil, &subpool, NULL); cbb->pool = subpool; - return rb_ensure((VALUE(*)(ANYARGS))callback, baton, - (VALUE(*)(ANYARGS))callback_ensure, subpool); + return rb_ensure(callback, baton, callback_ensure, subpool); } static VALUE -callback_handle_error(VALUE baton, ...) +callback_handle_error(VALUE baton) { callback_handle_error_baton_t *handle_error_baton; handle_error_baton = (callback_handle_error_baton_t *)baton; - return rb_rescue2((VALUE(*)(ANYARGS))callback, + return rb_rescue2(callback, (VALUE)(handle_error_baton->callback_baton), - (VALUE(*)(ANYARGS))callback_rescue, + callback_rescue, (VALUE)(handle_error_baton->rescue_baton), rb_svn_error(), (VALUE)0); @@ -1686,9 +1694,8 @@ invoke_callback_handle_error(VALUE baton, VALUE pool, svn_error_t **err) handle_error_baton.callback_baton = cbb; handle_error_baton.rescue_baton = &rescue_baton; - return rb_ensure((VALUE(*)(ANYARGS))callback_handle_error, - (VALUE)&handle_error_baton, - (VALUE(*)(ANYARGS))callback_ensure, pool); + return rb_ensure(callback_handle_error, (VALUE)&handle_error_baton, + callback_ensure, pool); } @@ -1721,7 +1728,7 @@ make_baton(apr_pool_t *pool, VALUE editor, VALUE baton) } static VALUE -add_baton_if_delta_editor(VALUE target, VALUE baton) +add_baton_if_delta_editor(RB_BLOCK_CALL_FUNC_ARGLIST(target, baton)) { if (RTEST(rb_obj_is_kind_of(target, svn_swig_rb_svn_delta_editor()))) { add_baton(target, baton); @@ -1741,8 +1748,7 @@ svn_swig_rb_set_baton(VALUE target, VALUE baton) target = rb_ary_new3(1, target); } - rb_iterate((VALUE(*)())rb_each, target, - (VALUE(*)())add_baton_if_delta_editor, baton); + rb_iterate(rb_each, target, add_baton_if_delta_editor, baton); } diff --git a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.h b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.h index bc8eb6e0a1df5..97bfeac128e07 100644 --- a/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.h +++ b/subversion/bindings/swig/ruby/libsvn_swig_ruby/swigutil_rb__pre_ruby.h @@ -57,7 +57,9 @@ typedef int gid_t; #if !defined(__cplusplus) && !defined(inline) #define inline __inline #endif -typedef long ssize_t; +#ifndef HAVE_SSIZE_T +typedef apr_ssize_t ssize_t; +#endif /* Don't define iovec when including APR */ #define APR_IOVEC_DEFINED @@ -92,6 +94,10 @@ typedef unsigned __int64 uint64_t; #ifdef _MSC_VER #pragma warning(disable: 4702) /* warning C4702: unreachable code */ +#if SWIG_VERSION < 0x040002 +/* Prevent C4113 "X differs in parameter lists from Y" errors */ +# pragma warning(default : 4113) +#endif #endif #endif /* defined(SVN_SWIG_RUBY__CUSTOM_RUBY_CONFIG) && defined(_MSC_VER) */ diff --git a/subversion/bindings/swig/ruby/svn/util.rb b/subversion/bindings/swig/ruby/svn/util.rb index f73554f5f7283..5f250be4d5d46 100644 --- a/subversion/bindings/swig/ruby/svn/util.rb +++ b/subversion/bindings/swig/ruby/svn/util.rb @@ -17,7 +17,7 @@ # under the License. # ==================================================================== -if /cygwin|mingw|mswin32|bccwin32/.match(RUBY_PLATFORM) +if /cygwin|mingw|mswin|bccwin32/.match(RUBY_PLATFORM) $LOAD_PATH.each do |load_path| svn_ext_path = File.join(load_path, "svn", "ext") if File.exist?(svn_ext_path) @@ -122,7 +122,7 @@ def filename_to_temp_file(filename) end def reset_message_directory - if /cygwin|mingw|mswin32|bccwin32/.match(RUBY_PLATFORM) + if /cygwin|mingw|mswin|bccwin32/.match(RUBY_PLATFORM) top_directory = File.join(File.dirname(__FILE__), "..", "..") top_directory = File.expand_path(top_directory) locale_directory = File.join(top_directory, "share", "locale") @@ -147,7 +147,7 @@ def validate_options(options, optional_keys, required_keys=[]) end def windows? - /cygwin|mingw|mswin32|bccwin32/.match(RUBY_PLATFORM) + /cygwin|mingw|mswin|bccwin32/.match(RUBY_PLATFORM) end end end diff --git a/subversion/bindings/swig/ruby/test/test_fs.rb b/subversion/bindings/swig/ruby/test/test_fs.rb index 8808055eec0a9..fa8f409c5f64f 100644 --- a/subversion/bindings/swig/ruby/test/test_fs.rb +++ b/subversion/bindings/swig/ruby/test/test_fs.rb @@ -219,18 +219,14 @@ def test_transaction end start_time = Time.now + sleep 0.032r if Svn::Util::windows? txn1 = @fs.transaction assert_equal([Svn::Core::PROP_REVISION_DATE], txn1.proplist.keys) assert_instance_of(Time, txn1.proplist[Svn::Core::PROP_REVISION_DATE]) date = txn1.prop(Svn::Core::PROP_REVISION_DATE) - # Subversion's clock is more precise than Ruby's on - # Windows. So this test can fail intermittently because - # the begin and end of the range are the same (to 3 - # decimal places), but the time from Subversion has 6 - # decimal places so it looks like it's not in the range. - # So we just add a smidgen to the end of the Range. - assert_operator(start_time..(Time.now + 0.001), :include?, date) + sleep 0.032r if Svn::Util::windows? + assert_operator(start_time..Time.now, :include?, date) txn1.set_prop(Svn::Core::PROP_REVISION_DATE, nil) assert_equal([], txn1.proplist.keys) assert_equal(youngest_rev, txn1.base_revision) @@ -420,10 +416,12 @@ def test_prop ctx.mkdir(["#{@wc_path}/new_dir"]) start_time = Time.now + sleep 0.032r if Svn::Util::windows? info = ctx.commit([@wc_path]) assert_equal(@author, info.author) assert_equal(@fs.youngest_rev, info.revision) + sleep 0.032r if Svn::Util::windows? assert_operator(start_time..(Time.now), :include?, info.date) assert_equal(@author, @fs.prop(Svn::Core::PROP_REVISION_AUTHOR)) diff --git a/subversion/bindings/swig/ruby/test/test_repos.rb b/subversion/bindings/swig/ruby/test/test_repos.rb index 1c0c366e627a5..5d0ba49679095 100644 --- a/subversion/bindings/swig/ruby/test/test_repos.rb +++ b/subversion/bindings/swig/ruby/test/test_repos.rb @@ -279,6 +279,7 @@ def assert_transaction assert_equal(prev_rev, @repos.youngest_rev) assert_equal(prev_rev, @repos.dated_revision(past_date)) + sleep 0.032r if Svn::Util::windows? prev_rev = @repos.youngest_rev @repos.transaction_for_commit(@author, log) do |txn| end diff --git a/subversion/bindings/swig/ruby/test/windows_util.rb b/subversion/bindings/swig/ruby/test/windows_util.rb index 338bf8fc31c27..6681a904a5e45 100644 --- a/subversion/bindings/swig/ruby/test/windows_util.rb +++ b/subversion/bindings/swig/ruby/test/windows_util.rb @@ -38,30 +38,25 @@ def setup_svnserve @svnserve_port = @svnserve_ports.last @repos_svnserve_uri = "svn://#{@svnserve_host}:#{@svnserve_port}" - @@service_created ||= begin - @@service_created = true + top_directory = File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..") + build_type = ENV["BUILD_TYPE"] || "Release" + svnserve_path = File.join(top_directory, build_type, 'subversion', 'svnserve', 'svnserve.exe') + svnserve_path = Svnserve.escape_value(svnserve_path) - top_directory = File.join(File.dirname(__FILE__), "..", "..", "..", "..", "..") - build_type = ENV["BUILD_TYPE"] || "Release" - svnserve_path = File.join(top_directory, build_type, 'subversion', 'svnserve', 'svnserve.exe') - svnserve_path = Svnserve.escape_value(svnserve_path) + root = @full_repos_path.tr(File::SEPARATOR, File::ALT_SEPARATOR) + FileUtils.mkdir_p(root) - root = @full_repos_path.tr(File::SEPARATOR, File::ALT_SEPARATOR) - FileUtils.mkdir_p(root) + IO.popen("#{svnserve_path} -d -r #{Svnserve.escape_value(root)} --listen-host #{@svnserve_host} --listen-port #{@svnserve_port} --pid-file #{@svnserve_pid_file}") - IO.popen("#{svnserve_path} -d -r #{Svnserve.escape_value(root)} --listen-host #{@svnserve_host} --listen-port #{@svnserve_port} --pid-file #{@svnserve_pid_file}") - user = ENV["USERNAME"] || Etc.getlogin + # Give svnserve a bit of time to start + sleep 1 - # Give svnserve a bit of time to start - sleep 1 - end true end def teardown_svnserve - # TODO: - # Load @svnserve_pid_file - # Kill process + pid = File.read(@svnserve_pid_file).to_i + Process.kill(:KILL, pid) end def add_pre_revprop_change_hook diff --git a/subversion/bindings/swig/svn_delta.i b/subversion/bindings/swig/svn_delta.i index 4ce7c83ad1f2a..e58606619a546 100644 --- a/subversion/bindings/swig/svn_delta.i +++ b/subversion/bindings/swig/svn_delta.i @@ -273,7 +273,7 @@ svn_swig_rb_delta_editor_get_target_revision(VALUE editor) if (NIL_P(rb_target_address)) return Qnil; - target_address = (svn_revnum_t *)(NUM2LONG(rb_target_address)); + target_address = (svn_revnum_t *)(NUM2SWIG(rb_target_address)); if (!target_address) return Qnil; diff --git a/subversion/bindings/swig/svn_wc.i b/subversion/bindings/swig/svn_wc.i index 1d7b461fa008e..2648f2e16126c 100644 --- a/subversion/bindings/swig/svn_wc.i +++ b/subversion/bindings/swig/svn_wc.i @@ -232,7 +232,7 @@ %typemap(argout) svn_revnum_t *target_revision { - %append_output(LONG2NUM((long)$1)); + %append_output(SWIG2NUM($1)); } #endif diff --git a/win-tests.py b/win-tests.py index 0581659281a75..23380cd91e98e 100644 --- a/win-tests.py +++ b/win-tests.py @@ -1344,7 +1344,7 @@ def stop(self): ruby_args = [ '-I', os.path.join(abs_srcdir, ruby_subdir), os.path.join(abs_srcdir, ruby_subdir, 'test', 'run-test.rb'), - '--verbose' + '--collector=dir', '--verbose', ] print('-- Running Swig Ruby tests --')