From af1d9faadb9aca329ea5796ab35c82f78a27f5e1 Mon Sep 17 00:00:00 2001 From: Jefferson Quesado Date: Sat, 28 Feb 2026 02:53:58 -0300 Subject: [PATCH 1/4] Making gem work with tcl/tk 9.0 --- ext/tk/extconf.rb | 14 +++---- ext/tk/tcltklib.c | 100 ++++++++++++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 42 deletions(-) diff --git a/ext/tk/extconf.rb b/ext/tk/extconf.rb index de2158bd..fb2ffc5f 100644 --- a/ext/tk/extconf.rb +++ b/ext/tk/extconf.rb @@ -12,12 +12,12 @@ # %w[8.9 8.8 8.7 8.6 8.5 8.4 8.3 8.2 8.1 8.0 7.6 4.2] # %w[8.7 8.6 8.5 8.4 8.3 8.2 8.1 8.0] # %w[8.7 8.6 8.5 8.4 8.0] # to shorten search steps - %w[8.6 8.5 8.4] + %w[8.6 8.5 8.4 9.0] TkLib_Config['unsupported_versions'] = %w[8.8 8.7] -TkLib_Config['major_nums'] = '87' +TkLib_Config['major_nums'] = '90' ############################################################## @@ -680,7 +680,9 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) # FIX ME: avoid pathname trouble (fail to find) on MinGW. $INCFLAGS << " -I" << File.join(File.dirname(File.dirname(file)),"include") if is_win32? else - tklibs = append_library("", libname) + # tklibs = append_library("", libname) + # there is no tk9.0 in Mac OS after Tcl/Tk 9.0 + tklibs = append_library("", "tcl9tk9.0") #tklibs = append_library("", $1) tklibs = "#{libpathflag([tkdir])} #{tklibs}" @@ -691,11 +693,7 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) tklibs << " " << tcllibs if tcllibs tmp_tklibs = tklibs.dup $LIBPATH = libpath | [tkdir] - try_func(tkfunc, tklibs, ["tcl.h", "tk.h"]) || - ( try_func(tkfunc, tklibs << " " << tkconf['TK_LIBS'], ["tcl.h", "tk.h"]) if tkconf['TK_LIBS'] ) || - ( try_func(tkfunc, (tklibs = tmp_tklibs.dup) << " " << tkconf['TK_XLIBSW'], ["tcl.h", "tk.h"]) if tkconf['TK_XLIBSW'] ) || - ( try_func(tkfunc, tklibs << " " << tkconf['TK_LIBS'], ["tcl.h", "tk.h"]) if tkconf['TK_LIBS'] ) - + have_header("tk.h") && have_library("tk", tkfunc, ["tcl.h", "tk.h"]) ensure mkmf_param = { 'PATH' => file, diff --git a/ext/tk/tcltklib.c b/ext/tk/tcltklib.c index 5f54b6f8..1d2b0368 100644 --- a/ext/tk/tcltklib.c +++ b/ext/tk/tcltklib.c @@ -131,7 +131,11 @@ set_tcltk_version(void) &(tcltk_version.type)); } -#if TCL_MAJOR_VERSION >= 8 +#if TCL_MAJOR_VERSION >= 9 +# define CONST const +# define CONST84 const +# define CONST86 const +#elif TCL_MAJOR_VERSION >= 8 # ifndef CONST84 # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 4 /* Tcl8.0.x -- 8.4b1 */ # define CONST84 @@ -152,6 +156,13 @@ set_tcltk_version(void) # endif #endif +/* let's deal with Tcl_Size */ +#if TCL_MAJOR_VERSION >= 9 +# define TYPE_TCL_SIZE Tcl_Size +#else +# define TYPE_TCL_SIZE int +#endif + #ifndef CONST86 # if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 5 /* Tcl8.0.x -- 8.5.x */ # define CONST86 @@ -1547,7 +1558,7 @@ call_original_exit(struct tcltkip *ptr, int state) #endif argv[0] = (char *)"exit"; /* argv[1] = Tcl_GetString(state_obj); */ - argv[1] = Tcl_GetStringFromObj(state_obj, (int*)NULL); + argv[1] = Tcl_GetStringFromObj(state_obj, (TYPE_TCL_SIZE*)NULL); argv[2] = (char *)NULL; ptr->return_value = (*(info->proc))(info->clientData, ptr->ip, 2, argv); @@ -3316,7 +3327,7 @@ ip_ruby_eval( #if TCL_MAJOR_VERSION >= 8 { char *str; - int len; + TYPE_TCL_SIZE len; thr_crit_bup = rb_thread_critical; rb_thread_critical = Qtrue; @@ -3424,7 +3435,7 @@ ip_ruby_cmd( volatile VALUE args; char *str; int i; - int len; + TYPE_TCL_SIZE len; struct cmd_body_arg *arg; int thr_crit_bup; VALUE old_gc; @@ -3579,7 +3590,7 @@ ip_RubyExitObjCmd( #if TCL_MAJOR_VERSION >= 8 /* cmd = Tcl_GetString(argv[0]); */ - cmd = Tcl_GetStringFromObj(argv[0], (int*)NULL); + cmd = Tcl_GetStringFromObj(argv[0], (TYPE_TCL_SIZE*)NULL); #endif if (argc < 1 || argc > 2) { @@ -3622,7 +3633,7 @@ ip_RubyExitObjCmd( return TCL_ERROR; } /* param = Tcl_GetString(argv[1]); */ - param = Tcl_GetStringFromObj(argv[1], (int*)NULL); + param = Tcl_GetStringFromObj(argv[1], (TYPE_TCL_SIZE*)NULL); #else /* TCL_MAJOR_VERSION < 8 */ state = (int)strtol(argv[1], &endptr, 0); if (*endptr) { @@ -3725,7 +3736,7 @@ ip_rbUpdateObjCmd( Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]"); #else # if TCL_MAJOR_VERSION >= 8 - int dummy; + TYPE_TCL_SIZE dummy; Tcl_AppendResult(interp, "wrong number of arguments: should be \"", Tcl_GetStringFromObj(objv[0], &dummy), " [ idletasks ]\"", @@ -3889,7 +3900,7 @@ ip_rb_threadUpdateObjCmd( Tcl_WrongNumArgs(interp, 1, objv, "[ idletasks ]"); #else # if TCL_MAJOR_VERSION >= 8 - int dummy; + TYPE_TCL_SIZE dummy; Tcl_AppendResult(interp, "wrong number of arguments: should be \"", Tcl_GetStringFromObj(objv[0], &dummy), " [ idletasks ]\"", @@ -3999,7 +4010,7 @@ ip_rbVwaitObjCmd( { int ret, done, foundEvent; char *nameString; - int dummy; + TYPE_TCL_SIZE dummy; int thr_crit_bup; DUMP1("Ruby's 'vwait' is called"); @@ -4232,7 +4243,8 @@ ip_rbTkWaitObjCmd( (char *) NULL }; enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW }; char *nameString; - int ret, dummy; + int ret; + TYPE_TCL_SIZE dummy; int thr_crit_bup; DUMP1("Ruby's 'tkwait' is called"); @@ -4601,7 +4613,7 @@ rb_threadVwaitProc( { struct th_vwait_param *param = (struct th_vwait_param *) clientData; - if (flags & (TCL_INTERP_DESTROYED | TCL_TRACE_DESTROYED)) { + if ((flags & TCL_TRACE_DESTROYED) || Tcl_InterpDeleted(interp)) { param->done = -1; } else { param->done = 1; @@ -4659,7 +4671,8 @@ ip_rb_threadVwaitObjCmd( { struct th_vwait_param *param; char *nameString; - int ret, dummy; + int ret; + TYPE_TCL_SIZE dummy; int thr_crit_bup; volatile VALUE current_thread = rb_thread_current(); struct timeval t; @@ -4818,7 +4831,8 @@ ip_rb_threadTkWaitObjCmd( (char *) NULL }; enum options { TKWAIT_VARIABLE, TKWAIT_VISIBILITY, TKWAIT_WINDOW }; char *nameString; - int ret, dummy; + int ret; + TYPE_TCL_SIZE dummy; int thr_crit_bup; volatile VALUE current_thread = rb_thread_current(); struct timeval t; @@ -5270,7 +5284,8 @@ delete_slaves(Tcl_Interp *ip) Tcl_Interp *slave; Tcl_Obj *slave_list, *elem; char *slave_name; - int i, len; + int i; + TYPE_TCL_SIZE len; DUMP1("delete slaves"); thr_crit_bup = rb_thread_critical; @@ -5290,7 +5305,7 @@ delete_slaves(Tcl_Interp *ip) /* get slave */ /* slave_name = Tcl_GetString(elem); */ - slave_name = Tcl_GetStringFromObj(elem, (int*)NULL); + slave_name = Tcl_GetStringFromObj(elem, (TYPE_TCL_SIZE*)NULL); DUMP2("delete slave:'%s'", slave_name); Tcl_DecrRefCount(elem); @@ -5668,7 +5683,7 @@ ip_rb_replaceSlaveTkCmdsObjCmd( #else char *nameString; #if TCL_MAJOR_VERSION >= 8 - nameString = Tcl_GetStringFromObj(objv[0], (int*)NULL); + nameString = Tcl_GetStringFromObj(objv[0], (TYPE_TCL_SIZE*)NULL); #else /* TCL_MAJOR_VERSION < 8 */ nameString = objv[0]; #endif @@ -5678,7 +5693,7 @@ ip_rb_replaceSlaveTkCmdsObjCmd( } #if TCL_MAJOR_VERSION >= 8 - slave_name = Tcl_GetStringFromObj(objv[1], (int*)NULL); + slave_name = Tcl_GetStringFromObj(objv[1], (TYPE_TCL_SIZE*)NULL); #else slave_name = objv[1]; #endif @@ -5772,7 +5787,7 @@ ip_rbNamespaceObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Ob for(i = 0; i < objc; i++) { /* argv[i] = Tcl_GetString(objv[i]); */ - argv[i] = Tcl_GetStringFromObj(objv[i], (int*)NULL); + argv[i] = Tcl_GetStringFromObj(objv[i], (TYPE_TCL_SIZE*)NULL); } argv[objc] = (char *)NULL; @@ -6029,7 +6044,12 @@ ip_init(int argc, VALUE *argv, VALUE self) #endif /* get main window */ mainWin = Tk_MainWindow(ptr->ip); - Tk_Preserve((ClientData)mainWin); +#if TCL_MAJOR_VERSION >= 9 +# define TCL_PRESERVE_FUNCTION Tcl_Preserve +#else +# define TCL_PRESERVE_FUNCTION Tk_Preserve +#endif + TCL_PRESERVE_FUNCTION((ClientData)mainWin); } /* add ruby command to the interpreter */ @@ -6099,7 +6119,12 @@ ip_init(int argc, VALUE *argv, VALUE self) Tcl_CallWhenDeleted(ptr->ip, ip_CallWhenDeleted, (ClientData)mainWin); if (mainWin != (Tk_Window)NULL) { - Tk_Release((ClientData)mainWin); +#if TCL_MAJOR_VERSION >= 9 +# define TCL_RELEASE_FUNCTION Tcl_Release +#else +# define TCL_RELEASE_FUNCTION Tk_Release +#endif + TCL_RELEASE_FUNCTION((ClientData)mainWin); } return self; @@ -6350,12 +6375,14 @@ ip_make_safe_core(VALUE interp, int argc /* dummy */, VALUE *argv /* dummy */) return rb_exc_new2(rb_eRuntimeError, "interpreter is deleted"); } - if (Tcl_MakeSafe(ptr->ip) == TCL_ERROR) { - /* return rb_exc_new2(rb_eRuntimeError, - Tcl_GetStringResult(ptr->ip)); */ - return create_ip_exc(interp, rb_eRuntimeError, "%s", - Tcl_GetStringResult(ptr->ip)); - } + /* Tcl_MakeSafe was removed, how to indicate this? */ + + /* if (Tcl_MakeSafe(ptr->ip) == TCL_ERROR) { + * * return rb_exc_new2(rb_eRuntimeError, + * Tcl_GetStringResult(ptr->ip)); * + * return create_ip_exc(interp, rb_eRuntimeError, "%s", + * Tcl_GetStringResult(ptr->ip)); + * } */ ptr->allow_ruby_exit = 0; @@ -6568,7 +6595,8 @@ ip_has_mainwindow_p(VALUE self) static VALUE get_str_from_obj(Tcl_Obj *obj) { - int len, binary = 0; + TYPE_TCL_SIZE len; + int binary = 0; const char *s; volatile VALUE str; @@ -7787,7 +7815,7 @@ lib_fromUTF8_core(VALUE ip_obj, VALUE src, VALUE encodename) if (strcmp(RSTRING_PTR(encodename), "binary") == 0) { Tcl_Obj *tclstr; char *s; - int len; + TYPE_TCL_SIZE len; StringValue(str); tclstr = Tcl_NewStringObj(RSTRING_PTR(str), RSTRING_LENINT(str)); @@ -8130,7 +8158,7 @@ ip_invoke_core(VALUE interp, int argc, char **argv) struct tcltkip *ptr; Tcl_CmdInfo info; char *cmd; - int len; + TYPE_TCL_SIZE len; int thr_crit_bup; int unknown_flag = 0; @@ -9200,7 +9228,7 @@ lib_split_tklist_core(VALUE ip_obj, VALUE list_str) #if TCL_MAJOR_VERSION >= 8 /* object style interface */ Tcl_Obj *listobj; - int objc; + TYPE_TCL_SIZE objc; Tcl_Obj **objv; int thr_crit_bup; @@ -9307,7 +9335,8 @@ ip_split_tklist(VALUE self, VALUE list_str) static VALUE lib_merge_tklist(int argc, VALUE *argv, VALUE obj) { - int num, len; + int num; + TYPE_TCL_SIZE len; int *flagPtr; char *dst, *result; volatile VALUE str; @@ -9399,7 +9428,8 @@ lib_merge_tklist(int argc, VALUE *argv, VALUE obj) static VALUE lib_conv_listelement(VALUE self, VALUE src) { - int len, scan_flag; + TYPE_TCL_SIZE len; + int scan_flag; volatile VALUE dst; int thr_crit_bup; @@ -9566,7 +9596,8 @@ update_encoding_table(VALUE table, VALUE interp, VALUE error_mode) { struct tcltkip *ptr; int retry = 0; - int i, idx, objc; + int i, idx; + TYPE_TCL_SIZE objc; Tcl_Obj **objv; Tcl_Obj *enc_list; volatile VALUE encname = Qnil; @@ -9847,7 +9878,8 @@ create_encoding_table_core(RB_BLOCK_CALL_FUNC_ARGLIST(arg, interp)) volatile VALUE table = rb_hash_new(); volatile VALUE encname = Qnil; volatile VALUE encobj = Qnil; - int i, idx, objc; + int i, idx; + TYPE_TCL_SIZE objc; Tcl_Obj **objv; Tcl_Obj *enc_list; From 05f68f1ff9270b8eb03d45f087fd9a2fad716ebe Mon Sep 17 00:00:00 2001 From: Jefferson Quesado Date: Mon, 2 Mar 2026 20:31:06 -0300 Subject: [PATCH 2/4] Fixing regex --- ext/tk/extconf.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/tk/extconf.rb b/ext/tk/extconf.rb index fb2ffc5f..aa7582f7 100644 --- a/ext/tk/extconf.rb +++ b/ext/tk/extconf.rb @@ -618,7 +618,7 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) tk_regexp = /^.*(tk#{stub}#{tkver}.*)\.(#{exts}).*$/ elsif tkconf tk_glob = "*tk#{stub}#{tkconf['TK_MAJOR_VERSION']}{.,}#{tkconf['TK_MINOR_VERSION']}*.*" - tk_regexp = /^.*(tk#{stub}#{tkconf['TK_MAJOR_VERSION']}(?:\.|)#{tkconf['TK_MINOR_VERSION']}.*)\.#{exts}.*$/ + tk_regexp = /^(?:.*(tcl#{tclconf['TCL_MAJOR_VERSION']})|.*)(tk#{stub}#{tkconf['TK_MAJOR_VERSION']}(?:\.|)#{tkconf['TK_MINOR_VERSION']}.*)\.#{exts}.*$/ end tcllib_ok ||= !tclconf || Dir.glob(File.join(tcldir, tcl_glob), File::FNM_CASEFOLD).find{|file| @@ -667,8 +667,8 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) tklib_ok ||= !tkconf || Dir.glob(File.join(tkdir, tk_glob), File::FNM_CASEFOLD).find{|file| if file =~ tk_regexp - libname = $1 - ext = $2.downcase + libname = ($1 || "") + $2 + ext = $3.downcase begin #puts "check #{file} #{$1} #{tkfunc} #{tkdir}" # find_library($1, tkfunc, tkdir) @@ -680,9 +680,7 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) # FIX ME: avoid pathname trouble (fail to find) on MinGW. $INCFLAGS << " -I" << File.join(File.dirname(File.dirname(file)),"include") if is_win32? else - # tklibs = append_library("", libname) - # there is no tk9.0 in Mac OS after Tcl/Tk 9.0 - tklibs = append_library("", "tcl9tk9.0") + tklibs = append_library("", libname) #tklibs = append_library("", $1) tklibs = "#{libpathflag([tkdir])} #{tklibs}" From 01c50c612ddae6ba147c4dc329e034ae5a32fe53 Mon Sep 17 00:00:00 2001 From: Jefferson Quesado Date: Tue, 3 Mar 2026 08:02:21 -0300 Subject: [PATCH 3/4] Simplifying regexes Thanks @jeremyevans! --- ext/tk/extconf.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/tk/extconf.rb b/ext/tk/extconf.rb index aa7582f7..58e26241 100644 --- a/ext/tk/extconf.rb +++ b/ext/tk/extconf.rb @@ -608,17 +608,17 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) if tclver tcl_glob = "*tcl#{stub}#{tclver}.*" - tcl_regexp = /^.*(tcl#{stub}#{tclver}.*)\.(#{exts}).*$/ + tcl_regexp = /(tcl#{stub}#{tclver}.*)\.(#{exts})/ elsif tclconf tcl_glob = "*tcl#{stub}#{tclconf['TCL_MAJOR_VERSION']}{.,}#{tclconf['TCL_MINOR_VERSION']}*.*" - tcl_regexp = /^.*(tcl#{stub}#{tclconf['TCL_MAJOR_VERSION']}(?:\.|)#{tclconf['TCL_MINOR_VERSION']}.*)\.(#{exts}).*$/ + tcl_regexp = /(tcl#{stub}#{tclconf['TCL_MAJOR_VERSION']}(?:\.|)#{tclconf['TCL_MINOR_VERSION']}.*)\.(#{exts})/ end if tkver tk_glob = "*tk#{stub}#{tkver}.*" - tk_regexp = /^.*(tk#{stub}#{tkver}.*)\.(#{exts}).*$/ + tk_regexp = /(tk#{stub}#{tkver}.*)\.(#{exts})/ elsif tkconf tk_glob = "*tk#{stub}#{tkconf['TK_MAJOR_VERSION']}{.,}#{tkconf['TK_MINOR_VERSION']}*.*" - tk_regexp = /^(?:.*(tcl#{tclconf['TCL_MAJOR_VERSION']})|.*)(tk#{stub}#{tkconf['TK_MAJOR_VERSION']}(?:\.|)#{tkconf['TK_MINOR_VERSION']}.*)\.#{exts}.*$/ + tk_regexp = /((?:tcl#{tclconf['TCL_MAJOR_VERSION']})?tk#{stub}#{tkconf['TK_MAJOR_VERSION']}\.?#{tkconf['TK_MINOR_VERSION']}.*)\.#{exts}/ end tcllib_ok ||= !tclconf || Dir.glob(File.join(tcldir, tcl_glob), File::FNM_CASEFOLD).find{|file| @@ -667,8 +667,8 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) tklib_ok ||= !tkconf || Dir.glob(File.join(tkdir, tk_glob), File::FNM_CASEFOLD).find{|file| if file =~ tk_regexp - libname = ($1 || "") + $2 - ext = $3.downcase + libname = $1 + ext = $2.downcase begin #puts "check #{file} #{$1} #{tkfunc} #{tkdir}" # find_library($1, tkfunc, tkdir) From 780bb81a9cf7caa641ac80a1cbe6c2821878aaae Mon Sep 17 00:00:00 2001 From: Jefferson Quesado Date: Tue, 3 Mar 2026 08:58:08 -0300 Subject: [PATCH 4/4] Actually, it seems I was wrong about this It seems that `try_func` is indeed generating the right code --- ext/tk/extconf.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ext/tk/extconf.rb b/ext/tk/extconf.rb index 58e26241..de865395 100644 --- a/ext/tk/extconf.rb +++ b/ext/tk/extconf.rb @@ -691,7 +691,11 @@ def libcheck_for_tclConfig(tcldir, tkdir, tclconf, tkconf) tklibs << " " << tcllibs if tcllibs tmp_tklibs = tklibs.dup $LIBPATH = libpath | [tkdir] - have_header("tk.h") && have_library("tk", tkfunc, ["tcl.h", "tk.h"]) + try_func(tkfunc, tklibs, ["tcl.h", "tk.h"]) || + ( try_func(tkfunc, tklibs << " " << tkconf['TK_LIBS'], ["tcl.h", "tk.h"]) if tkconf['TK_LIBS'] ) || + ( try_func(tkfunc, (tklibs = tmp_tklibs.dup) << " " << tkconf['TK_XLIBSW'], ["tcl.h", "tk.h"]) if tkconf['TK_XLIBSW'] ) || + ( try_func(tkfunc, tklibs << " " << tkconf['TK_LIBS'], ["tcl.h", "tk.h"]) if tkconf['TK_LIBS'] ) + ensure mkmf_param = { 'PATH' => file,