Dear all, Tcl/Tk on MacOS challenges me: * an old version of TclTk (8.5) as default on MacOS * build applications for the MacOS platform ** and especially sign a complete app to be recognized on MacOS * and maybe also some performance issue. Let us talk about our experience and best practices. Manfred ---- '''[ManfredR] - 2025-02-16 18:00:00''' **Build MacOS-App based on Tcl/Tk-Framework** after long nights of trial and error and with the support from Kevin, Paul and Alexander I can provide a solution for this issue here: * Kevin Walzer [https://www.codebykevin.com] * Paul Obermeier [https://www.tcl3d.org/bawt] * Alexander Schoepe [https://www.sowaswie.de/tcl-tk] ***MacOS*** The MacOS, I build this intstruction ====== uname -a Darwin … 24.3.0 Darwin Kernel Version 24.3.0: Thu Jan 2 20:24:06 PST 2025; root:xnu-11215.81.4~3/RELEASE_ARM64_T8103 arm64 ====== ***Sources:*** https://www.tcl-lang.org/software/tcltk/download.html * tcl8.6.16-src.tar.gz [http://prdownloads.sourceforge.net/tcl/tcl8.6.16-src.tar.gz] * tk8.6.16-src.tar.gz [http://prdownloads.sourceforge.net/tcl/tk8.6.16-src.tar.gz] ***Tools:*** ====== which gcc /usr/bin/gcc which python3 /Library/Frameworks/Python.framework/Versions/3.10/bin/python3 which unzip /usr/bin/unzip which install_name_tool /usr/bin/install_name_tool which otool /usr/bin/otool # unzip tk8.6.16-src.tar.gz ====== ====== gcc --version Apple clang version 16.0.0 (clang-1600.0.26.6) Target: arm64-apple-darwin24.3.0 Thread model: posix InstalledDir: /Library/Developer/CommandLineTools/usr/bin python3 --version Python 3.10.5 ====== ***Prepare to compile a Tcl/Tk-Framework*** ****Sources**** extract sources for tcl and tk: ====== .../myWorkDir │ ├── tcl8616 │ ├── tcl8.6.16 │ │ ├── compat │ │ ├── doc │ │ ├── generic │ │ ├── library │ │ ├── libtommath │ │ ├── macosx │ │ ├── pkgs │ │ │ ├── itcl4.3.2 │ │ │ ├── sqlite3.47.2 │ │ │ ├── tdbc1.1.10 │ │ │ └── ... │ │ ├── tests │ │ ├── tools │ │ ├── unix │ │ └── win │ │ │ └── tk8.6.16 │ ├── bitmaps │ ├── compat │ ├── doc │ ├── generic │ ├── library │ └── ... │ └── tclbuild.sh ====== ****tclbuild.sh**** based on a template I found here: * stackoverflow.com [https://stackoverflow.com/questions/53777217/find-precompiled-or-compile-tcl-tk-frameworks-for-macos] * tclbuild.sh ====== #!/bin/bash # # ./tclbuild.sh # macosxminver=11.0 sver=8616 ver=8.6.16 mver=8.6 tclmver=$mver tkmver=$mver SRCDIR=$HOME/Development/tcl_lang/tcl${sver} INSTLOC=$HOME/Development/tcl_lang/install if [[ $1 != "" ]]; then INSTLOC=$1 fi if [[ -d $INSTLOC ]]; then rm -rf $INSTLOC fi mkdir $INSTLOC cd $SRCDIR test -d build && rm -rf build cd $SRCDIR cd tcl${ver} if [[ $? -eq 0 ]]; then f=library/init.tcl if [[ ! -f $f-orig ]]; then cp -pf $f $f-orig fi cp -pf $f-orig $f make -C macosx \ PREFIX="" \ CFLAGS_OPTIMIZE=" -arch x86_64 -arch arm64 -mmacosx-version-min=${macosxminver}" \ INSTALL_ROOT=$INSTLOC install # CFLAGS_OPTIMIZE="-O2 -mmacosx-version-min=${macosxminver}" cd $SRCDIR chmod u+w $INSTLOC/bin/tclsh${tclmver} install_name_tool -change \ "/Library/Frameworks/Tcl.framework/Versions/${tclmver}/Tcl" \ @executable_path/../Library/Frameworks/Tcl.framework/Versions/${tclmver}/Tcl \ $INSTLOC/bin/tclsh${tclmver} fi cd $SRCDIR cd tk${ver} if [[ $? -eq 0 ]]; then make -C macosx \ PREFIX="" \ CFLAGS_OPTIMIZE=" -arch x86_64 -arch arm64 -mmacosx-version-min=${macosxminver}" \ INSTALL_ROOT=$INSTLOC install # CFLAGS_OPTIMIZE="-O2 -mmacosx-version-min=${macosxminver}" cd $SRCDIR chmod u+w $INSTLOC/Library/Frameworks/Tk.framework/Versions/${tkmver}/Resources/Wish.app/Contents/MacOS/Wish install_name_tool -change \ "/Library/Frameworks/Tk.framework/Versions/${tkmver}/Tk" \ @executable_path/../../../../Tk \ $INSTLOC/Library/Frameworks/Tk.framework/Versions/${tkmver}/Resources/Wish.app/Contents/MacOS/Wish install_name_tool -change \ "/Library/Frameworks/Tcl.framework/Versions/${tclmver}/Tcl" \ @executable_path/../../../../../../../Tcl.framework/Versions/${tclmver}/Tcl \ $INSTLOC/Library/Frameworks/Tk.framework/Versions/${tkmver}/Resources/Wish.app/Contents/MacOS/Wish fi cd $SRCDIR find $INSTLOC -type f -print0 | xargs -0 chmod u+w exit 0 ====== ****compile Tcl/Tk-Framework**** let the compiler do its job now … ====== cd .../myWorkDir/ %./tclbuild.sh >> ... >> ... >> >> scanning section Tk Commands, version 8.6.16 >> shuffling ttk_widget.n to front of processing queue >> shuffling options.n to front of processing queue >> ..................................... >> menubar: make-manpage-section: discarding menubar >> ...... >> pack-old: make-manpage-section: discarding pack-old >> ............................................ >> scanning section Tk C API, version 8.6.16 >> ....................................................................................... >> Assembling index >> Rescanning 173 pages to build cross links and write out >> ...................................................................................................................... >> Done ====== ***Result of compilation:*** ====== .../myWorkDir │ ├── tcl8616 │ ├── build │ ├── tcl8.6.16 │ └── tk8.6.16 │ └── install │ ├── Applications │ └── Utilities │ ├── Wish\ Shell.app ---> Wish.app │ └── Wish.app ---> .//../..//Library/Frameworks/Tk.framework/Resources/Wish.app ├── Library │ ├── Frameworks │ │ ├── Tcl.framework │ │ │ ├── Headers ---> Versions/Current/Headers │ │ │ ├── PrivateHeaders ---> Versions/Current/PrivateHeaders │ │ │ ├── Resources ---> Versions/Current/Resources │ │ │ └── Versions │ │ │ ├── 8.6 │ │ │ │ ├── Headers │ │ │ │ ├── PrivateHeaders │ │ │ │ ├── Resources │ │ │ │ │ ├── Documentation │ │ │ │ │ │ └── Reference │ │ │ │ │ │ └── Tcl │ │ │ │ │ │ └── ... │ │ │ │ │ ├── Scripts │ │ │ │ │ │ ├── encoding │ │ │ │ │ │ ├── http1.0 │ │ │ │ │ │ └── opt0.4 │ │ │ │ │ └── tcl8 │ │ │ │ │ ├── 8.4 │ │ │ │ │ ├── 8.5 │ │ │ │ │ └── 8.6 │ │ │ │ └── pkgconfig │ │ │ └── Current ---> 8.6 │ │ └── Tk.framework │ │ ├── Headers ---> Versions/Current/Headers │ │ ├── PrivateHeaders ---> Versions/Current/PrivateHeaders │ │ ├── Resources ---> Versions/Current/Resources │ │ └── Versions │ │ ├── 8.6 │ │ │ ├── Headers │ │ │ │ └── X11 │ │ │ ├── PrivateHeaders │ │ │ ├── Resources │ │ │ │ ├── Documentation │ │ │ │ │ └── Reference │ │ │ │ │ └── Tk │ │ │ │ │ └── ... │ │ │ │ ├── Scripts │ │ │ │ │ └── ... │ │ │ │ ├── Wish Shell.app ---> Wish.app │ │ │ │ └── Wish.app │ │ │ │ └── Contents │ │ │ │ ├── MacOS │ │ │ │ └── Resources │ │ │ └── pkgconfig │ │ └── Current ---> 8.6 │ └── Tcl │ ├── itcl4.3.2 │ ├── sqlite3.47.2 │ ├── tcl8 │ ├── tdbc1.1.10 │ ├── tdbcmysql1.1.10 │ ├── tdbcodbc1.1.10 │ ├── tdbcpostgres1.1.10 │ └── thread2.8.11 ├── bin ├── include └── man ├── man3 └── mann ====== ****Test the Result**** We are looking for a Wish.app ====== cd .../myWorkDir/ open ./install/Applications/Utilities/Wish.app # is a link to: ./../../Library/Frameworks/Tk.framework/Resources/Wish.app # ... failed ??? open ./install/Library/Frameworks/Tk.framework/Resources/Wish.app # is a link to: Versions/Current/Resources # ... OK open ./install/Library/Frameworks/Tk.framework/Versions/Current/Resources/Wish.app # ... OK # ... this is the template we will use in the next step # ====== **Extract a standalone Wish.app** ***What is needed*** … a standalone Wish.app with a given structure. We name it myApp.app. ====== .../myWorkDir │ └── myApp.app └── Contents ├── Info.plist ├── Frameworks/ │ ├── Tcl.framework/ │ ├── Tk.framework/ ├── Tcl │ └── ... ├── MacOS/ │ └── myApp └─── Resources/ └── myApp.icns ====== ****Create myApp.app**** We use the compiled result as a base. ====== cd .../myWorkDir cp -Rf ./install/Library/Frameworks/Tk.framework/Resources/Wish.app ./myApp.app mkdir -p ./myApp.app/Contents/Frameworks cp -Rf ./install/Library/Frameworks/Tk.framework ./myApp.app/Contents/Frameworks cp -Rf ./install/Library/Frameworks/Tcl.framework ./myApp.app/Contents/Frameworks mkdir -p ./myApp.app/Contents/Library cp -Rf ./install/Library/Tcl ./myApp.app/Contents/Library/Tcl ====== Now we have to test our app ====== open ./myApp.app # # -> failed # ====== ****Debug our App**** … but don’t panic ====== # # ------------------------------------- # Translated Report (Full Report Below) # ------------------------------------- # # Process: Wish [5329] # Path: .../*/Wish.app/Contents/MacOS/Wish # Identifier: com.tcltk.wish # Version: 8.6.16 (8.6.16) # Code Type: ARM-64 (Native) # # Crashed Thread: 0 # # Exception Type: EXC_CRASH (SIGABRT) # Exception Codes: 0x0000000000000000, 0x0000000000000000 # # Termination Reason: Namespace DYLD, Code 1 Library missing # Library not loaded: @executable_path/../../../../Tk # Referenced from: <E91A1C5F-BF22-3AB4-87AA-F312F03B9FDE> .../*/myApp.app/Contents/MacOS/Wish # Reason: tried: '.../Tk' (no such file) # (terminated at launch; ignore backtrace) # ====== The reason for this, are the libraries searched for in our executable in our app. ====== otool -L ./myApp.app/Contents/MacOS/Wish # # /Users/manfred/Development/tcl_lang/Wish.app/Contents/MacOS/Wish (architecture x86_64): # @executable_path/../../../../Tk (compatibility version 8.6.0, current version 8.6.16) # @executable_path/../../../../../../../Tcl.framework/Versions/8.6/Tcl (...) # /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0) # ... # ====== We see that the executable is unable to find Tk and Tcl. We have to change these references: ====== install_name_tool -change \ @executable_path/../../../../Tk \ @executable_path/../Frameworks/Tk.framework/Versions/8.6/Tk \ ./myApp.app/Contents/MacOS/Wish install_name_tool -change \ @executable_path/../../../../../../../Tcl.framework/Versions/8.6/Tcl \ @executable_path/../Frameworks/Tcl.framework/Versions/8.6/Tcl \ ./myApp.app/Contents/MacOS/Wish ====== We test again ====== open ./myApp.app # # -> OK # ====== [https://i.imgur.com/Glw8gZL.png|png|jpg] ****Make our app running Tcl-Code**** ... myApp.app should not only work as an interpreter. It shall run our own Tcl/Tk-Application. We must extend our app with some tcl-Scripts: * AppMain.tcl (mandatory) and * myApp.tcl ====== .../myWorkDir │ └── myApp.app └── Contents ├── Info.plist ├── Frameworks │ ├── Tcl.framework │ └── Tk.framework ├── Library │ └── Tcl │ └── ... ├── MacOS │ └── myApp └── Resources ├── myApp.icns └── Resources │ └── Scripts └── AppMain.tcl ====== … but we'll do a little more ====== .../myWorkDir │ └── myApp.app └── Contents ├── Info.plist ├── Frameworks/ │ ├── Tcl.framework/ │ └── Tk.framework/ ├── MacOS/ │ └── myApp ├── Library │ └── Tcl │ ├── itcl4.3.2 │ ├── sqlite3.47.2 │ ├── tcl8 │ └── ... └── Resources/ ├── myApp.icns └── Resources │ └── Scripts ├── AppMain.tcl ├── bin │ └── myApp.tcl └── lib └── ... ====== so we have to prepare our app and add some files ====== mkdir -p ./myApp.app/Contents/Resources/Scripts/bin mkdir -p ./myApp.app/Contents/Resources/Scripts/lib touch ./myApp.app/Contents/Resources/Scripts/AppMain.tcl touch ./myApp.app/Contents/Resources/Scripts/bin/myApp.tcl # vi ./myApp.app/Contents/Resources/Scripts/AppMain.tcl # vi ./myApp.app/Contents/Resources/Scripts/bin/myApp.tcl ====== …/Scripts/AppMain.tcl ====== # # …/Scripts/AppMain.tcl # # set __script_Dir___ [file dirname [info script]] set __script__Tcl__ [file normalize [file join $__script_Dir___ .. .. Library Tcl]] set __script__Lib__ [file normalize [file join $__script_Dir___ lib]] # lappend auto_path $__script__Tcl__ lappend auto_path $__script__Lib__ # puts " -- \$auto_path -- " foreach dir $auto_path { puts " $dir" } # # ... check availability of itcl # if [file exists [file join $__script__Tcl__ itcl4.3.2]] { set env(ITCL_LIBRARY) [file join $__script__Tcl__ itcl4.3.2] } else { puts " package: itcl " puts " ... \$env(ITCL_LIBRARY) ... not available" } # # ... check availability of Tk # # if [file exists [file join $__script__Lib__ itk4.2.5]] { # set env(ITK_LIBRARY) [file join $__script__Lib__ itk4.2.5] # } else { # puts " package: itk " # puts " ... \$env(ITK_LIBRARY) ... not available" # } # puts " -- \$__script_Dir___ --" puts " $__script_Dir___ \n" puts "" # source [file join $__script_Dir___ bin myApp.tcl] # # package require starkit # starkit::startup # source [file join $__script_Dir___ __myApp.kit] # ====== …/Scripts/bin/myApp.tcl ====== # # …/Scripts/bin/myApp.tcl # # package require Tk # Create the main window wm title . "myApp @ Tcl/Tk - Framework-Wish.app" wm geometry . 600x300+100+100 # Procedure to report Environment to the Text widget proc reportEnv {} { .textWidget delete 1.0 end .textWidget insert end "\n" .textWidget insert end "--- auto_path ---\n" .textWidget insert end "\n" foreach dir $::auto_path { .textWidget insert end " ... $dir\n" } .textWidget insert end "\n" .textWidget insert end "--- env ---\n" .textWidget insert end "\n" .textWidget insert end [format { ... %-15s -> %s} ITCL_LIBRARY $::env(ITCL_LIBRARY)]\n .textWidget insert end "\n" .textWidget insert end "--- env ---\n" .textWidget insert end "\n" foreach name [array names ::env] { .textWidget insert end [format { ... %-15s -> %s} $name $::env($name)]\n } .textWidget insert end "\n\n ... done!\n" } # Procedure to load Packages proc requirePackages {} { puts "\n" puts " ... load packages" puts "" puts " ... load Itcl" package require Itcl puts "" puts " ... done" } # Create a button to report Environment button .buttonEnv -text " report Environment " -command {reportEnv} # Create a button to require Packages button .buttonPkg -text " require Packages " -command {requirePackages} # Create a frame to hold the Text widget and scrollbars # frame .textFrame # Create a Text widget with scrollbars text .textWidget -width 120 -height 25 -yscrollcommand {.yScroll set} -xscrollcommand {.xScroll set} scrollbar .yScroll -orient vertical -command {.textWidget yview} scrollbar .xScroll -orient horizontal -command {.textWidget xview} grid .textWidget .yScroll -sticky news grid .xScroll -sticky news # Configure grid weights grid rowconfigure . 0 -weight 1 grid columnconfigure . 0 -weight 1 # Grid the button in the main window grid .buttonEnv -row 2 -column 0 -columnspan 2 -pady 2 -ipadx 10 grid .buttonPkg -row 3 -column 0 -columnspan 2 -pady 2 -ipadx 10 ====== …/Scripts/lib ... reserved to place additional packages required by your application and are not provided by this TclTk-Framework ====== tcllib, tdom, ... ====== Test your application ====== open ./myApp.app # # -> OK # ====== [https://i.imgur.com/3Jrf5Md.png|png|jpg] **Codesigning and notarization** Before start codesigning you have to unlock your signing-keychain ====== security unlock-keychain login.keychain # # -> requires a Developer-ID and a password # ====== Lets do the codesigning not on the original myApp but do it in a copy. The following manual is inspired from a proposal of Kevin Walzer [https://www.codebykevin.com] ***Requirements*** ****Apple Developer ID:**** ====== security find-identity -p codesigning Policy: Code Signing Matching identities 1) … 2) … 3) ABC12345A1B23456789012345678901234567890 "Developer ID Application: Small Feather (1AB2CD3EF5)" 3 identities found Valid identities only 1) ABC12345A1B23456789012345678901234567890 "Developer ID Application: Small Feather (1AB2CD3EF5)" 1 valid identities found ====== ****Notarization Profile**** ====== xcrun notarytool store-credentials <profile-name> \ --apple-id "<your-apple-id>" \ --team-id "<your-team-id>" \ --password "<your-app-specific-password>" xcrun notarytool store-credentials "myNotarizationProfile" \ --apple-id "[email protected]" \ --team-id "1AB2CD3EF5" \ --password "abcd-efgh-ijkl-mnop" # # in this manual: <profile-name> myNotarizationProfile # ====== ****entitlements.plist**** ... please care about unix linefeeds !?!? ... do not ask!! ====== .../myWorkDir │ ├── tcl8616 ├── install ├── myApp.app │ └── ... ├── build │ └── myApp.app ├── tclbuild.sh │ └── entitlements.plist ====== ./entitlements.plist ====== <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd"> <plist version="0.9"> <dict> <key>com.apple.security.app-sandbox</key> <false/> <key>com.apple.security.automation.apple-events</key> <true/> <key>com.apple.security.cs.allow-jit</key> <true/> <key>com.apple.security.cs.disable-library-validation</key> <true/> <key>com.apple.security.get-task-allow</key> <true/> </dict> </plist> ====== ***... now step by step *** ====== cd .../myWorkDir mkdir build cp -Rf myApp.app ./build/myApp.app # # ... cleanup ./build/myApp.app # xattr -cr ./build/myApp.app # ... if neccessary # python3 -mmacholib standalone ./build/myApp.app rm -rf ./build/myApp.app/Contents/Frameworks/Tcl.framework/Versions/8.6/Resources/Documentation rm -rf ./build/myApp.app/Contents/Frameworks/Tk.framework/Versions/8.6/Resources/Documentation rm -rf ./build/myApp.app/Contents/Frameworks/Tk.framework/Versions/8.6/Resources/Wish.app rm -rf ./build/myApp.app/Contents/Frameworks/Tk.framework/Versions/8.6/Resources/"Wish Shell.app" rm -rf ./build/myApp.app/Contents/MacOS/"Wish Shell" find ./build/myApp.app -name "*.a" -exec rm -rf {} \; find ./build/myApp.app -name "*debug" -exec rm -rf {} \; find ./build/myApp.app -name "*.sh" -exec rm -rf {} \; chmod -R a+rw ./build/myApp.app # # ... codesign ./build/myApp.app # find ./build/myApp.app -type f -name "*.bundle" -exec \ codesign --verbose -f \ --signature-size 9400 \ --sign "Developer ID Application" {} \; find ./build/myApp.app -type f -name "*.dylib" -exec \ codesign --verbose -f \ --signature-size 9400 \ --sign "Developer ID Application" {} \; codesign --verbose -f \ --signature-size 9400 \ --sign "Developer ID Application" \ ./build/myApp.app/Contents/Frameworks/Tk.framework/Versions/8.6 codesign --verbose -f \ --signature-size 9400 \ --sign "Developer ID Application" \ ./build/myApp.app/Contents/Frameworks/Tcl.framework/Versions/8.6 codesign --verbose=2 -f --deep --timestamp \ --signature-size 9400 \ --sign "Developer ID Application" \ --options runtime \ --entitlements ./entitlements.plist \ ./build/myApp.app # # ... check entitlements # codesign -d --entitlements - ./build/myApp.app # # ... create a zip for notarization # cd ./build ditto -c -k --sequesterRsrc --keepParent \ myApp.app \ myApp_Setup.zip # # ... notarize your app # xcrun notarytool submit myApp_Setup.zip \ --keychain-profile myNotarizationProfile \ --wait # # ... in notarization was successful, staple your app # xcrun stapler staple myApp.app # # … build a .dmg-container to deliver your app # hdiutil create -srcfolder myApp.app -volname myApp_Demo myApp_Demo_$$.dmg cd ../ # # ... Done. # ====== ***some additional commands*** ====== plutil myApp.app/Contents/Info.plist lldb myApp.app ... ====== ---- **Discussions** [TR]: my experience: * do not use the built-in version but install your own recent Tcl 8.6 (I just compile from the sources myself) * build applications using the bundle facility of macOS, here is a recipe -> https://www.codebykevin.com/tutorial.html * code-signing a macOS app can be done with a self-signed signature using the `codesign` utility (`codesign --force --deep -s - $exeFile`) but this will still trigger a warning as the certificate is not a paid one (you only get full peace of mind when you pay Apple $ 100 per year) * What particular performance issues do you have? Otherwise, make sure to check the [New Tcl/TkAqua FAQ] (and yes, it needs an update!) ---- '''[ManfredR] - 2025-01-10 21:55:20''' I tried to codesign "vanillawish","tclkit" and a single "wish" and "tclsh". my observation: * I could codesign "wish" and "tclsh" * but could not codesign "vanillawish" and "tclkit". I fear this observation is based on, that codesign does not sign binaries with a payload. If this is the case "vanillawish" and "tclkit" are no oportunity to provide a tcl-runtime with batteries included for MacOS apps. * the --deep option should no longer be used ---- [ALX] I assume that all files embedded into the VFS must be code-signed first. Afterwards, the executable needs to be signed. ---- '''[ManfredR] - 2025-01-29 15:00:00''' I found an approach used to build '''tkChat '''for MacOS: * TclApps Library Source Code [https://core.tcl-lang.org/tclapps/file?name=apps/tkchat/deploy/buildapp_mac&ci=tip] and a manual * Building Stand-Alone Tcl/Tk Applications under Mac OS X [https://wiki.tcl-lang.org/page/Building+Stand-Alone+Tcl%2FTk+Applications+under+Mac+OS+X] This approach requires '''wish.app''' as template ====== wish.app/ Contents/ Info.plist Frameworks/ Tcl.framework/ Tk.framework/ MacOS/ wish Resources/ wish.icns ====== to use this template with the name '''myApp''' all '''whish''' should be renamed to e.g. '''myApp''' ====== myApp.app/ Contents/ Info.plist Frameworks/ Tcl.framework/ Tk.framework/ MacOS/ myApp Resources/ myApp.icns ====== ... and if I got it right: ====== ./myApp.app/Contents/MacOS/myApp ====== is a nearly native ====== wish ====== looking for a tcl-script ====== ./myApp.app/Resources/Scripts/AppMain.tcl (or main.tcl???) ====== my Question: ... is there anybody out there, providing a wish.app as a template (for tcl 8.6)? ---- [ABU] 16-02-2025 Instead of building a tcltk interpreter for MacOS (it's always a long and complex task), I just downloaded a "single-file-snapshot' from github. This is a fully working 'wish' 9.0.2 https://github.com/tcltk/tk/actions/runs/13280568013 Then I can start to create macOS App, just by packing this executable and my specific scripts (Tcl plus some binary libraries). Well , this is just the start of the story .. the problem is that I have no intention to pay a Developer License to Apple just to deliver some open-source apps. Here is a full dmg you can download, install and run ... but there're many manual steps required you should do to pass all the security checks. https://sourceforge.net/projects/irrational-numbers/files/Caligraft/Caligraft2025.dmg/download I will be grateful to anyone who tries to test this app. (Note: it's for Intel-Mac ; I don't know how it works on new Apple processors). [TR] - 2025-02-16 I have tried to open the app on macOS 15.2 (Sequoia; M2 processor). I was able to get it running, finally. First, I got the message that "Apple could not check whether the app is free from malicious software". I could circumvent this with the usual methods (context menu). Also, calling the wrapper under "Contents > MacOS" directly from the command line did not help. I then needed to go into the system settings, and under privacy I needed to explicitly allow the app. After this, it worked. reply to [TR] - 2025-02-16 by [ManfredR] for MacOS Sequioa * its confusing, but make it run on your "Developer Mac" does not mean that the same app will also run on another Mac * you have to codesign and notarize your app, to prevent the Systems & Security settings [ABU] Great ! Could you explain what you did into system settings ? I've heard something like "xattr -d com.apple.quarantine ..all files.." , isn't it enough ? Just last question (some beta users told me it didn't work ...) : First, you run the 'main app' (it's just a simple Tk panel with some images), but then, were you able to launch the real demos by double-clicking over one of the images ? [TR] - I used the GUI app and navigated to "Privacy $ Security" (it might be called slightly different - I am using a German macOS), then I scrolled Doen to the bottom of that page. There, macOS lists the Caligraft app (since I had tried to open it, otherwise it would not pop up there) and gives me the opportunity to to click on a button to allow running this app anyway). The method using `xattr` is nice but nothing you can tell your normal users ... I was not able to run the real demos. Double-clicking results in another instance if Calicraft launching (so it seems judging be the icon that comes up) which then died after less than a second. I see no other window. However, when I start it from the command line, I get this: ====== Error in startup script: dlopen(/Applications/Caligraft2025.app/Contents/Caligraft2025/lib/blend2d-1.4/darwin-x64/tkb2d90.dylib, 0x0006): tried: '/Applications/Caligraft2025.app/Contents/Caligraft2025/lib/blend2d-1.4/darwin-x64/tkb2d90.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e' or 'arm64')), '/System/Volumes/Preboot/Cryptexes/OS/Applications/Caligraft2025.app/Contents/Caligraft2025/lib/blend2d-1.4/darwin-x64/tkb2d90.dylib' (no such file), '/Applications/Caligraft2025.app/Contents/Caligraft2025/lib/blend2d-1.4/darwin-x64/tkb2d90.dylib' (mach-o file, but is an incompatible architecture (have 'x86_64', need 'arm64e' or 'arm64')) while executing "load [BL::_findDLL $dir "tkBlend2d"] T2d" (lambda term " dir { source [file join $dir t2d.tcl] load [BL::_findDLL..." line 3) invoked from within "apply { dir { source [file join $dir t2d.tcl] load [BL::_findDLL $dir "tkBlend2d"] T2d package provide tkBlend2d 1.4 }} /Applications/Caligraft202..." ("package ifneeded tkBlend2d 1.4" script) invoked from within "package require -exact tkBlend2d $ver" (lambda term " dir { set ver 1.4 package require -exact tkBlend2d $ver ..." line 3) invoked from within "apply { dir { set ver 1.4 package require -exact tkBlend2d $ver package provide Blend2d $ver }} /Applications/Caligraft2025.app/Contents/Caligraft..." ("package ifneeded Blend2d 1.4" script) invoked from within "package require Blend2d 1.4" (file "/Applications/Caligraft2025.app/Contents/Caligraft2025/lalana-v1.tcl" line 45) invalid command name "::tkerror" while executing "::tkerror $err" ====== So, as I have an arm machine, I need the library compiled for arm, not for x86. That is because the app launches as an arm app (you have provided a fat binary with both architectures so the native one will take preceedence. But then of course, all libraries also need the same architecture, which the hay not. ---- [ManfredR] - 2025-02-19 Is it an approach to have every binary compiled for intel as long as not all tcl-packages (libraries) are also available for the arm architecture although your mac is a newer one? MacOS handles opening your intel based app via Rosetta mode ... I am looking for fat binaries (arm & intel) of: * tcltls (8.6 & 9.0 ... to connect to web-services and * vqtcl (8.6 & 9.0 ... to open starkit from Wish.app ---- [ManfredR] - 2025-02-18 ====== % pwd /Volumes/Caligraft Installer ====== 1) Info.plist ====== % cat Caligraft2025.app/Contents/Info.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleName</key> <string>xCaligraft</string> <key>CFBundleExecutable</key> <string>wrapper</string> <key>CFBundleIdentifier</key> <string>abu-irrational.github.com.Caligraft</string> <key>CFBundleVersion</key> <string>1.2.3</string> <key>CFBundleIconFile</key> <string>Caligraft2025.icns</string> </dict> </plist> ====== 2) Caligraft2025.app/Contents/MacOS/wrapper ... is a shell script ====== #!/bin/bash DIR="$(dirname "$0")" $DIR/wish902 $DIR/../Caligraft2025/Caligraft.tcl ====== 3) Caligraft2025.app ====== % lldb Caligraft2025.app (lldb) target create "Caligraft2025.app" error: '/Volumes/Caligraft Installer/Caligraft2025.app/Contents/MacOS/wrapper' doesn't contain any 'host' platform architectures: arm64, armv7, armv7f, armv7k, armv7s, armv7m, armv7em, armv6m, armv6, armv5, armv4, arm, thumbv7, thumbv7k, thumbv7s, thumbv7f, thumbv7m, thumbv7em, thumbv6m, thumbv6, thumbv5, thumbv4t, thumb, x86_64, x86_64, arm64, arm64e, arm64, arm64e (lldb) ====== my conclusion: * since Macos Sequoia MacOS expects '''CFBundleExecutable''': .../MacOS/'''wrapper ''' as a binary and not as a shell-script (see: lldb Caligraft2025.app) * a script can not provide required platform architectures * MacOS Sequioa also does not accept a starpack as '''CFBundleExecutable''', raises an error when codesigning * therfore this approach using Wish.app with its Tcl & Tk-Framework structure * e.g. have a look on the TkChat.app http://tkchat.tcl-lang.org/%|%TkChat.app%|% The proposed structure for '''Caligraft''' could look like this. I havent done it yet, but follow the same steps as in this manual with Tcl9 ====== .../myWorkDir │ └── Caligraft.app └── Contents ├── Info.plist ├── Frameworks/ │ ├── Tcl.framework/ │ └── Tk.framework/ ├── MacOS/ │ └── Caligraft (... just rename the file located here and update the reference in Info.plist) ├── Library │ └── Tcl │ ├── itcl4.3.2 │ ├── sqlite3.47.2 │ ├── tcl8 │ └── ... └── Resources/ ├── Caligraft .icns └── Resources │ └── Scripts ├── AppMain.tcl (... update this script to point to ./bin/Caligraft.kit) ├── bin │ └── Caligraft.kit └── lib └── ... ====== ... maybe you want to send me a personal message [ManfredR] ---- [ManfredR] - 2025-02-17 if an app is not codesigned and notarized by apple, MacOS sees your app as a threat. in Systems Privacy .. you can define an exception to run your app. the second point is if you make your app run on your Mac does not mean that the same app runs on an other Mac. ... but works if it is signed and notarized only [ABU] in reply to [ManfredR] I know and agree on the code-signed Apps. This is a question to solve, because I don't want to pay Apple for delivering free and open-source code, so, my advice was simply to find a way to deliver a beta-version to people with a low technical/hacking profile. Second point: what do you mean "-- if you make your app run on your Mac does not mean that the same app runs on an other Mac" ? I delivered an App with no external dependencies; A full 'wish' interpreter, with all the required libraries (tcl and dylib) included. I built this App on my Intel-Mac, and then, as [TR] pointed out above, it works even on M2-Mac. Well .. not on every Mac, and this is hard to discover, since I don't have another Mac; I'm just trying to collect response from beta-users. All these troubles because *today* MacOS is still equipped with TclTk 8.5 !! There's no official/supported TclTk App for MacOS, and we cannot ask users to build Tcl and Tk themself. [ManfredR] in reply to [ABU] "if you make your app run on your Mac does not mean that the same app runs on an other Mac" Its the restrictive gatekeeper of MacOS Sequioa. For Test-Purpose I borrowed a MacOS from https://www.macincloud.com/%|%macincloud%|%. It's just as confusing for me. I can't do the final user test for my app on my own Mac Tcl 8.5 on Mac: https://brew.sh/%|%Homebrew%|% brings Tcl 9.0 to MacOS allready ---- [ALX] - 2025-02-17 What [ManfredR] has compiled here is truly a valuable contribution – a big kudos for that. The core issue is that macOS classifies programs that are not officially signed as potentially unsafe or even as malware. When I compile a program on my Mac, it is usually locally signed, so my system accepts the corresponding signature as trustworthy. However, when the same program is run on another Mac, this trust-building signature is missing, and the tightened security mechanisms kick in – often in the form of warning messages or even blocking of execution. These security precautions have been continuously tightened over the years to prevent the spread of malware. A comparable approach is now also found in Windows, where unsigned or not trustworthily certified programs are also considered potentially dangerous. ---- ---- '''[NR] - 2025-02-18 09:16:03''' ABU, you can try this (not tested) : https://signpath.org/ [ABU] This is a great idea, except that they require a complete CI Workflow to be implemented. And to do that, this would cost me much, much more than paying $100 for an Apple certificate. ---- ''' [ManfredR] 2025-02-19 commenting [ABU] 16-02-2025''' >>> Instead of building a tcltk interpreter for MacOS (it's always a long and complex task), I just downloaded a "single-file-snapshot' from github. This is a fully working 'wish' 9.0.2 ====== https://github.com/tcltk/tk/actions/runs/13280568013 ====== is there also a link to download a snapshot? [ABU] Yes, but you need to be logged-in to download the snapshots. [ManfredR] Thanks. Did anyone try to build a MacOs-App based on this Wish?