Vala 與 GObject Introspection 的不搭配狀況
Vala 與 GObject Introspection 都是 GNOME 3 平台中的重要角色。 Vala 為 C 語言程序員提供了易用的 GObject 設計工具。GObject Introspection 則是銜結不同程式語言所實作的 GObject 項目的橋樑。我在《ICOS 2010 記事》中,便提到像 gjs/seed 這些新興的 JavaScript 解譯器,可以透過 GObject Introspection 調用既有的 GLib 項目。 理論上這兩套工具要能搭配互補,但我這陣子在摸索它們的互補途徑時,卻碰了一鼻子灰。
軟體版本
- Ubuntu Lucid
- valac 0.12.0
- gobject-introspection 0.6.8-1
hello.vala 實作了一個非常簡單的類別。我將用它展示我碰到的狀況。
下列步驟可以編出 libhello.so 以及一份 GObject Introspection 所需的 gir 文件。
$ valac --library=hello --gir=Hello-0.1.gir \
-X -shared -X -fPIC -o libhello.so hello.vala
狀況1: g-ir-compiler 不接受 valac 產生的 gir 文件
我先將上一節由 valac 產出的 Hello-0.1.gir 複製為 Hello-1.0.gir,再交給 g-ir-compiler 編譯為 typelib 中介連結檔。遺憾的是,它開頭就抱怨 gir 文件的版本不對。
$ cp Hello-0.1.gir Hello-1.0.gir
$ g-ir-compiler --output=Hello-1.0.typelib Hello-1.0.gir
Error at line 3, character 1: Unsupported version '1.2'
...
(其餘省略)
如果只是版本不符的問題那還好辦,然而事情卻沒那麼簡單。仔細修改下來後,我發現 valac 所產出的 gir 文件內容,根本不合 g-ir-compiler 的預期。
在 mailing-list 上,Vala 作者曾抱怨 GObject Introspection 缺乏公開文件,他必須自己解析已有的 gir 文件反推其義。所以才會造成現在這種不相容的狀況。
狀況2: g-ir-scanner 掃描 hello.c 所產生的 gir 文件,沒有類別內容
既然 valac 產出的 gir 文件不合用,那我們再換個方式。我們讓 valac 產出 C 源碼,再將之交由 g-ir-scanner 掃描內容,由 g-ir-scanner 產出 gir 文件。
$ valac -C --library=hello hello.vala
$ LD_LIBRARY_PATH=. g-ir-scanner --namespace=Hello --nsversion=1.0 \
--include=GObject-2.0 --pkg=gobject-2.0 --library=hello hello.c \
-o Hello-0.2.gir
$ cat Hello-0.2.gir
...
(沒有類別內容)
查看了 g-ir-scanner 的輸出結果,我卻發現它沒有類別資訊。
依 GObject Introspection Annotations 之說明,程式源碼中要有 Gtk-doc 的註記才能被 g-ir-scanner 處理。而 valac 產生的 C 源碼中,並沒有幫我們產生這些註記,是以 g-ir-scanner 也不會產出類別資訊於 gir 文件。
附帶一提, g-ir-scanner 的 --library-path 參數似乎沒有作用。就算我指定了這個參數,它仍然找不到 libhello.so。 於是我直接以環境變數 LD_LIBRARY_PATH 指定 libhello.so 所在路徑。
手動編輯 Hello-1.0.gir
由於以上兩種工具都不能幫我產生可用的 gir 文件,所以我目前只能自己動手編輯了。 這個工作缺乏有效的參考文件,只能邊改邊嘗試。
以下為我編輯出來符合 Hello 類別的 gir 文件。
$ g-ir-compiler --output=Hello-1.0.typelib Hello-1.0.gir
狀況3: Vala 定義的類別內容,無法透過 GObject Introspection 機制配置實體
雖然我千辛萬苦地弄出了 gir 文件,並產出了 typelib 的中介連結檔。但是實際使用時,卻不能配置出實體。 以下以 gjs 為示範:
執行結果:
$ gjs test-hello.js
(gjs-console.real:12062): GLib-GObject-CRITICAL **: g_object_newv: assertion `G_TYPE_IS_OBJECT (object_type)' failed
(gjs-console.real:12062): GLib-GObject-CRITICAL **: g_object_is_floating: assertion `G_IS_OBJECT (object)' failed
(其餘省略)
但是直接用 C 語言,按照 GObject 設計慣例 所實作的類別內容,使用相同的 Hello-1.0.gir,卻可正常配置實體。
編譯與執行結果如下列所示:
$ gcc `pkg-config --cflags --libs gobject-introspection-1.0` \
hello_pure.c -shared -fPIC -o libhello.so
$ g-ir-compiler --output=Hello-1.0.typelib Hello-1.0.gir
$ gjs test-hello.js
GJS: namespace of Hello: [object GIRepositoryNamespace]
class_init
obj_init
GJS: type of o is [object instance proxy GIName:Hello.Hello jsobj]
hello world
我雖然嘗試比較過 Vala 產出的 hello.c 與我直接用 C 語言撰寫的 hello_pure.c,但目前還看不出是在哪個環節有差異,才導致 Vala 產生的 GObject 類別不能透過 GObject Introspection 實體化。
但是 Vala 設計的一般函數內容(不歸屬於類別的純函數),其他語言仍可透過 GObject Introspection 調用。
Vala 與 GObject Introspection 這兩套工具,目前還有許多地方未能互相搭配。要讓它們在 GNOME 3 平台上共舞,現階段似乎還不是時候。