ulua 路徑小記 以及 lua require 機制整理

ulua 路徑小記

在學習ulua時,require模塊的根路徑能夠爲項目的Lua文件夾或者ToLua文件夾(Editor下),可是在package.pathpackage.cpath中並無看到當前項目的路徑,那require是如何找到Lua和ToLua文件夾的路徑的呢?html

  • ulua在初始化LuaState時,會先解析package.path, 並將其存入LuaFileUtilsSearchPaths中(LuaState.cs line:603),並將Lua和ToLua的目錄添加其中(LuaState.cs line:187)。
  • 在LuaState的構造函數中,會調用ToLua.OpenLib(T),該方法定義了一下特殊方法,能夠在lua中使用,如typeof等。
  • OpenLib方法會在package.loaders數組中,再添加一個loader,而且將其放到數組的第二個位置。(ToLua.cs line:192)注:Lua require模塊時,會依次調用package.loader中的方法,找到則返回
  • loader的做用就是檢測傳入的文件名(module名),是否在LuaFileUtilsSearchPaths,若是存在就讀取文件,若不存在則返回空(LuaFileUitls.cs line:170)

Lua require 相關整理(基於lua 5.1)

摘自http://cloudwu.github.io/lua53doc/manual.html#6.3
根據lua5.1 手冊作了部分修改git

package.preload

爲指定的模塊,設置loader,在require模塊時,先去查詢這張表,若是有值,則使用preload中的loader(能夠用於修改特殊模塊的加載策略)github

package.path

這個路徑被 require 在 Lua 加載器中作搜索時用到。
在啓動時,Lua 用環境變量 LUA_PATH來初始化這個變量。 或採用 luaconf.h 中的默認路徑。 環境變量中出現的全部 ";;" 都會被替換成默認路徑。數組

package.cpath

這個路徑被 require 在 C 加載器中作搜索時用到。函數

Lua 用和初始化 Lua 路徑 package.path 相同的方式初始化 C 路徑 package.cpath 。 它會使用環境變量LUA_CPATH初始化。 要麼就採用 luaconf.h 中定義的默認路徑。學習

package.loaders(Lua5.3中, 更名爲searchers)

用於 require控制如何加載模塊的表。ui

這張表內的每一項都是一個 查找器函數。 當查找一個模塊時, require 按次序調用這些查找器, 並傳入模塊名(require的參數)做爲惟一的一個參數。 此函數能夠返回另外一個函數(模塊的 加載器)加上另外一個將傳遞給這個加載器的參數。 或是返回一個描述爲什麼沒有找到這個模塊的字符串 (或是返回 nil 什麼也不想說)。lua

Lua 用四個查找器函數初始化這張表。3d

第一個查找器就是簡單的在 package.preload 表中查找加載器。code

第二個查找器用於查找 Lua 庫的加載庫。 它使用儲存在 package.path 中的路徑來作查找工做。路徑是一個包含有一系列以分號分割的 模板 構成的字符串。 對於每一個模板,都會用 name 替換其中的每一個問號(若是有的話)。 且將其中的 點替換爲系統的目錄分割符(如 Unix中的"/")。 而後嘗試打開這個文件名。

例如,若是路徑是字符串

"./?.lua;./?.lc;/usr/local/?/init.lua"

搜索 foo.a 這個名字將 依次嘗試打開文件 ./foo/a.lua , ./foo/a.lc ,以及 /usr/local/foo/a/init.lua

第三個查找器用於查找 C 庫的加載庫。 它使用儲存在 package.cpath中的路徑來作查找工做。 例如,若是 C 路徑是這樣一個字符串

"./?.so;./?.dll;/usr/local/?/init.so"

查找器查找模塊 foo 會依次嘗試打開文件 ./foo.so./foo.dll, 以及 /usr/local/foo/init.so。 一旦它找到一個 C 庫, 查找器首先使用動態連接機制鏈接該庫。 而後嘗試在該庫中找到能夠用做加載器的 C 函數。 這個 C 函數的名字是 "luaopen_" 緊接模塊名的字符串, 其中字符串中全部的下劃線都會被替換成點。 此外,若是模塊名中有橫線, 橫線後面的部分(包括橫線)都被去掉。 例如,若是模塊名爲 a.b.c-v2.1, 函數名就是luaopen_a_b_c

第四個搜索器是 一體化加載器。 它從 C 路徑中查找指定模塊的根名字。 例如,當請求 a.b.c 時, 它將查找 a 這個 C 庫。 若是找獲得,它會在裏面找子模塊的加載函數。 在咱們的例子中,就是找 luaopen_a_b_c。 利用這個機制,能夠把若干 C 子模塊打包進單個庫。 每一個子模塊均可以有本來的加載函數名。

require(modname)

加載一個模塊。 這個函數首先查找 package.loaded表, 檢測 modname 是否被加載過。 若是被加載過,require 返回 package.loaded[modname] 中保存的值。(防止重複加載) 不然,它試着爲模塊尋找 加載器

首先 require 查找 package.preload[modname] 。 若是這裏有一個值,這個值(必須是一個函數)就是那個加載器。 不然 require 使用 Lua 加載器去查找 package.path 的路徑。 若是查找失敗,接着使用 C 加載器去查找 package.cpath 的路徑。 若是都失敗了,再嘗試 一體化 加載器 (參見 package.loaders

每次找到一個加載器,require 都用一個參數調用加載器: modname 。 若是加載器返回非空值, require 將這個值賦給package.loaded[modname]。 若是加載器沒能返回一個非空值用於賦給 package.loaded[modname]require 會在那裏設入 true 。 不管是什麼狀況,require 都會返回 package.loaded[modname] 的最終值。

若是在加載或運行模塊時有錯誤, 或是沒法爲模塊找到加載器, require 都會拋出錯誤。

相關文章
相關標籤/搜索