通常、Tcl/Tk を使用する場合、 lib フォルダ下に encodingファイル群、msgsファイル群、tzdataファイル群、tclスクリプト群、... その他の沢山のファイルを配置する必要があります。 Tcl/Tk を使った C or C++ アプリケーションを配布したい場合、exe ファイルだけでなく、これら Tcl/Tk のファイルも一緒に配布する必要があるので、 ちょっとしたアプリを作ってインストール、という使い方をしたい場合は、いまいち使いづらいかもしれません。
これら Tcl/Tk ファイル群が1つにまとまっていたら通常の dll ライブラリと同じ感覚で扱え、より手軽になるはずです。
Tcl/Tk のファイルを全てまとめて1つの実行ファイルにする方法として、 Starpack 等がありますが、そこまでやらなくても、tarやzip等の一般的なアーカイブ形式で 上記ファイル群をまとめておき、実行時に必要な部分だけ読み込みできるだけでも、かなり手軽になると思います。 さらに、一般的なアーカイブ形式の方が拡張の追加や不要な部分の削除等の操作が容易になるというメリットも生じます。
TclVfs 拡張を用いることで、tar、zip等のアーカイブファイルの中身やhttp、ftp等で通信可能なWebサイトを 任意のディレクトリにマウントできるようなので、本稿ではこれを活用して、Tcl/Tk のファイル群を tar にまとめて、 起動時にマウントして読み込む方法を紹介します。
tar ファイルをマウントして、中に入っているスクリプト等を読み込むためには、 TclVfs 拡張と Memchan 拡張が必要です。 従って、まず、Tcl/Tk に この 2 つの拡張を導入します。
Visual Studioを使っている方で、TclVfs 拡張をソースからビルドして導入する方法は TclTk + Incr Tcl + Incr Tk + Incr Widgets + TclVfs + Metakit を VisualC++ を使ってソースから導入するメモ が役に立つかもしれません。 Tcl/Tkのビルドからのやり方を記載しています。
('13/12/18 時点でダウンロードできる最新版 tclvfs-20080503 は、TclTk 8.5.10、8.5.11 でビルドOK、8.5.14、8.5.15でビルドNGを確認しました。 対策は、 VS2008でTclVFSをtcl.8.5.15に対応させる方法をご参照下さい。)
work / tcl8.5.10 / winとかunixとか... / tk8.5.10 / winとかunixとか... / tclvfs-20080503 / winとかmacとか... / Memchan2.3 / winとかmacとか...
<mk_memchan.bat のコマンドライン引数の説明>
1つ目 : Release版か、Debug版かを指定します。 Release または Debug を指定してください。 2つ目 : ビットモードを指定します。 x86 または x64 を指定してください。 実行例: mk_memchan.bat Release x86
inst フォルダの中に、tcltk 実行のために必要なファイル群が全て入っていることを確認したら、 inst / lib フォルダ下の中身を tar アーカイブにします。
tar ファイル作成の際、下記事項に注意してください。
dde1.3/ dde1.3/pkgIndex.tcl Memchan2.3/ ...下記のようになっている場合はNGです。
lib/dde1.3/ lib/dde1.3/pkgIndex.tcl lib/Memchan2.3/ ...
Tcl_Init 実行時に、 tar ファイルをマウントして、その中にある init.tcl ファイルを読みに行かせたいため、その前の段階で vfs::tar が動くような仕掛けを導入します。
tar ファイルをマウントするコマンド vfs::tar::Mount を動作させるためには、 tarvfs.tcl と vfsUtils.tcl を先に読み込む必要があるようなので、 これらを tar アーカイブから直接抽出します。
コーディング例は下記の通りです。tar ファイルに、 tk のデモも一緒に入れたと想定して、そのデモファイルを実行するところまで 記述してあります。他の拡張も導入したい場合は、Tcl_StaticPackage や ~_Init 関数を追記することになります。
下記の例のソースコードは こちらからダウンロード することもできます。 著作権、ライセンス表示(Boost ver.1 ライセンス)を追加した以外は同一のものです。
#include "tcl.h" #include "tk.h" #include <string> #include <cstdio> extern "C" { int Vfs_Init(Tcl_Interp *); int Memchan_Init(Tcl_Interp *); } std::string read_vfs_script(const char *tarfile){ if (!tarfile) return ""; FILE *fp = std::fopen(tarfile, "rb"); if (!fp) return ""; std::string script; char buf[513]; buf[512] = '\0'; int datablocks = 0; long filesize = 0; bool add_to_script = false; while(!std::feof(fp)){ if (datablocks == 0){ // header size_t sz = fread(buf, 512, 1, fp); char typeflag = buf[156]; switch(typeflag){ case '1': case '2': case '3': case '4': case '5': case '6': case '7': continue; } filesize = std::strtol(buf + 124, 0, 8); datablocks = (filesize + 511) / 512; add_to_script = (std::strstr(buf, "vfs1.3/tarvfs.tcl") != 0) || (std::strstr(buf, "vfs1.3/vfsUtils.tcl") != 0); }else if (add_to_script){ size_t sz = std::fread(buf, 512, 1, fp); --datablocks; if (!add_to_script) continue; if (filesize < 512) buf[filesize] = '\0'; script += buf; filesize -= 512; }else{ std::fseek(fp, datablocks * 512, SEEK_CUR); datablocks = filesize = 0; } } std::fclose(fp); return script; } int main(int argc, char *argv[]){ Tcl_Interp *interp = ::Tcl_CreateInterp(); // 2017/1/23 追記 ... Tcl_FindExecutable を下に移動したからここでは呼びたくない・・・が、コメントアウトしても大丈夫だろうか・・・ // Tcl_FindExecutable(argv[0]); int rc; ::Tcl_StaticPackage(interp, "Tcl", Tcl_Init, 0); ::Tcl_StaticPackage(interp, "Tk", Tk_Init, 0); ::Tcl_StaticPackage(interp, "vfs", Vfs_Init, 0); ::Tcl_StaticPackage(interp, "memchan", Memchan_Init, 0); // 実行ファイルのディレクトリを取得 std::string exedir = argv[0]; exedir = exedir.substr(0, exedir.find_last_of('\\')+1); for (size_t i = 0; i < exedir.size(); ++i) if (exedir[i] == '\\') exedir[i] = '/'; ::Tcl_SetVar2(interp, "tcl_library", 0, (exedir + "lib/tcl8.5/").c_str(), TCL_GLOBAL_ONLY); // tarファイル をマウントするために必要なモジュール群をロード rc = ::Vfs_Init(interp); rc = ::Memchan_Init(interp); // tarファイル をマウントするために必要なスクリプトをtarファイルの中から見つけて取得 std::string scr = read_vfs_script((exedir + "tcl_rt.tar").c_str()); rc = ::Tcl_Eval(interp, scr.c_str()); // tarファイル をマウント rc = ::Tcl_Eval(interp, "package require vfs::tar;"); rc = ::Tcl_Eval(interp, "vfs::tar::Mount $tcl_library/../../tcl_rt.tar $tcl_library/../;"); // Tcl、Tk の初期化 rc = ::Tcl_Init(interp); rc = ::Tk_Init(interp); // 2017/1/23 追記 ... Tcl、Tk 初期化後でないと、 system encoding の登録が利かない模様 Tcl_FindExecutable(argv[0]); // デモを動かす Tcl_Eval(interp, "source $tcl_library/../tk8.5/demos/widget;"); Tk_MainLoop(); return 0; }
上記ソースをビルドした結果作成される exe と、 tar ファイルを同一のフォルダに配置します。 また、vfs 拡張や memchan 拡張の Cソースをビルドした部分を dll として扱いたい場合は、先ほど tar 化する際に除いた lib フォルダ下の各 dll ファイルも、 exe ファイルと同じディレクトリに配置して下さい。
[PageInfo]
LastUpdate: 2017-01-23 00:01:30, ModifiedBy: mocchi_2012
[Permissions]
view:all, edit:admins, delete/config:admins