模組版本控管 |
|
_G
表) 包含一個 _VERSION
變數,設定為 "Lua 5.1"
或 "Lua 5.2"
[1]。許多其他模組遵循類似慣例,儲存 _VERSION
變數 (以及其他變數,如 _NAME
) 在其模組表中。「1.2.3b」等版本可能不是十進制數字,因此通常儲存為字串。這些字串的比較可能不那麼直接,可能需要特殊剖析/比較函式,例如 LuaRocks [deps.lua] 中的 (參閱 [Rockspec 格式]) 或 [簡單版本排序]。如果遵循某些較嚴格的慣例,則可以使用簡單字串比較 (例如,"010.001.001" > "009.005.001"
) 或「自然比較」[3] (compare("10.1.1", "9.5.1")
)。儲存為數字 (整數或浮點數) 的版本也可以進行簡單比較 (010001001 > 009005001
和 10.001.001 > 9.005001
),但零填補使其更難閱讀且可能容易輸入錯誤。如果使用整數編譯 Lua,儲存為浮點數的版本號碼將會出現問題,而且不存在將浮點數格式化為字串的單一標準方式(例如「1.010」對「1.01」對「1.010E+00」)。版本也可以表示為表格 [4] ( {1,2,3,'b'}
或 {major=1,minor=2,micro=3,stage='b'}
),也可以透過從某個版本控制模組載入其元表上的比較運算子:_VERSION = require 'someversionlib' '1.2.3b'
。
以下是一些從 LuaDist 儲存庫中的模組掃描的範例
$ cd Repository $ grep -re 'VERSION *=[^=]' --include '*.lua' ./copas/tests/cosocket.lua:_VERSION = "0.1" ./copas/src/copas/copas.lua:_VERSION = "Copas 1.1.7" ./wsapi-xavante/src/wsapi/common.lua:_G.wsapi._VERSION = "WSAPI 1.3.4" ./wsapi-xavante/src/wsapi/sapi.lua: _VERSION = "WSAPI SAPI 1.0", ./lemock/build/lemock.lua:_VERSION = "LeMock 0.6" ./cgilua/src/cgilua/cgilua.lua:_VERSION = "CGILua 5.1.4" ./oil/lua/luaidl.lua:VERSION = '1.0.5' ./oil/lua/socket/url.lua:_VERSION = "URL 1.0.1" ./oil/lua/oil/compat.lua:VERSION = "OiL 0.5 beta" ./oil/lua/luaidl/lex.lua:PRAGMA_VERSION = '1.0' ./oil/lua/oil.lua:VERSION = "OiL 0.5" ./luajson/util/createRock.lua: VERSION = ("%q"):format(version), ./xavante/src/xavante/xavante.lua:_VERSION = "Xavante 2.2.0" ./lualogging/src/logging/logging.lua:_VERSION = "LuaLogging 1.1.4" ./lua_uri/uri.lua:local M = { _NAME = "uri", VERSION = "1.0" } ./luasoap/soap.lua:_VERSION = "1.0b" ./getopt/getopt.lua:_VERSION = "0.1.1" The version number can be used to check version consistency at runtime, to advise a module to use a different version of an interface, or to load a different version of the module. For checking, sometimes you see things like local foo = require 'foo'; assert(foo._VERSION >= 1.23), though possibly replacing the '>=' test with something that parses the version nu ./vstruct/vstruct/init.lua:_VERSION = "1.1" ./dado/src/dado/object.lua:_VERSION = "Dado Object 1.2.0" ./dado/src/dado/sql.lua:_VERSION = "Dado SQL 1.2.0" ./dado/src/dado.lua:_VERSION = "Dado 1.2.0" ./remdebug/src/remdebug/engine.lua:_VERSION = "1.0" ./luasql-sqlite/src/ado/ado.lua:luasql._VERSION = "LuaSQL 2.1.1" ./luasql-sqlite/src/jdbc/src/lua/jdbc.lua:luasql._VERSION = "LuaSQL 2.0.2" ./luaidl/luaidl.lua:VERSION = '0.8.9b' ./luaidl/luaidl/lex.lua:PRAGMA_VERSION = '1.0' ./wsapi-fcgi/src/wsapi/common.lua:_G.wsapi._VERSION = "WSAPI 1.3.4" ./wsapi-fcgi/src/wsapi/sapi.lua: _VERSION = "WSAPI SAPI 1.0", ./shake/src/shake/shake.lua:_VERSION = "Shake 1.0.2" ./luasocket/src/url.lua:_VERSION = "URL 1.0.1" ./luasocket/src/ltn12.lua:_VERSION = "LTN12 1.0.1" ./venv/src/stable.lua:_VERSION = "Stable 1.0" ./luasql-mysql/src/ado/ado.lua:luasql._VERSION = "LuaSQL 2.1.1" ./luasql-mysql/src/jdbc/src/lua/jdbc.lua:luasql._VERSION = "LuaSQL 2.0.2" ./penlight/lua/pl/utils.lua:utils._VERSION = "0.9.0" ./abelhas/pso.lua:VERSION = "1.0" ./luadoc/src/luadoc/init.lua:_VERSION = "LuaDoc 3.0.1" ./lanes/tests/assert.lua: VERSION= 20070603, -- last change (yyyymmdd) ./rings/src/stable.lua:_VERSION = "Stable 1.0" ./wsapi/src/wsapi/common.lua:_G.wsapi._VERSION = "WSAPI 1.3.4" ./wsapi/src/wsapi/sapi.lua: _VERSION = "WSAPI SAPI 1.0", ./luaglut/glut_test1.lua:print('_VERSION = ' .. _VERSION) ./luaglut/glut_test1.lua:print('luagl.VERSION = ' .. luagl.VERSION) ./luaglut/glut_test1.lua:print('luaglut.VERSION = ' .. luaglut.VERSION) ./luaglut/glut_test2.lua:print('_VERSION = ' .. _VERSION) ./luaglut/glut_test2.lua:print('luagl.VERSION = ' .. luagl.VERSION) ./luaglut/glut_test2.lua:print('luaglut.VERSION = ' .. luaglut.VERSION) ./luasec/src/https.lua:_VERSION = "0.4" ./luasec/src/ssl.lua:_VERSION = "0.4" ./luasql-sqlite3/src/ado/ado.lua:luasql._VERSION = "LuaSQL 2.1.1" ./luasql-sqlite3/src/jdbc/src/lua/jdbc.lua:luasql._VERSION = "LuaSQL 2.0.2" ./wxlua/bindings/genwxbind.lua:WXLUA_BINDING_VERSION = 27 -- Used to verify that the bindings are updated ./luaxmlrpc/xmlrpc.lua:_VERSION = "1.0b"
例如,
-- copas/src/copas/copas.lua ..... module ("copas", package.seeall) -- Meta information is public even if beginning with an "_" _COPYRIGHT = "Copyright (C) 2005-2010 Kepler Project" _DESCRIPTION = "Coroutine Oriented Portable Asynchronous Services" _VERSION = "Copas 1.1.7" .....
-- penlight/lua/pl/utils.lua ..... local utils = {} utils._VERSION = "0.9.0" .....
在許多情況下,專案名稱 (大小寫混合,可能不如模組名稱 _NAME
具體) 會包含在版本號碼前面,類似 _G._VERSION
所做的那樣。[我不知道為什麼值得這樣做,而不是使用單獨變數,--DM]
LuaDistributions(例如 LuaRocks、LuaDist、debian 等) 通常會套用版本控制架構,但通常是在套件層級而不是在模組層級(其中一個套件可能包含多個模組)。維持模組和套件版本相同會是一個好主意。此外,可以透過修補上游來取得發行套件,因此 LuaRocks 會將其自己的連字號和數字附加到上游設定的版本中(例如,2.0.1 可能會變成 2.0.1-1,如 [Rockspec 格式] 中所討論的)。
版本編號可用於在執行時期檢查版本的一致性,通知模組使用介面的不同版本,或載入模組的不同版本。有時候在檢查的時候,您會看到像這樣的內容:`local foo = require 'foo'; assert(foo._VERSION >= 1.23)`,儘管可能使用解讀版本號字串的內容替換 '>=
' 測試。在 Perl 中,有點類似於 Lua `require` 的 `use` 陳述式可以傳遞版本號碼(例如,"use foo 1.23;
" [2]),轉送至模組,而模組實際上可以對它做任何它想做的事(例如,檢查它或更動介面行為)。在 Lua 中,可能會透過 `local foo = require 'foo' (1.23)` 執行類似的事情,但通常不會看到這種情形。LuaRocks 提供可選的備用形式 `require`,它不僅會檢查還會建議載入哪個版本,而這取決於已安裝多少個版本([luarocks.require]),但現在這種用法並不多見。
從模組中擷取版本通常會載入模組和讀取其 `_VERSION` 變數。如果模組不可信,您可以載入它到 sandbox 中,或只執行基礎的原始碼分析(ProgramAnalysis)來避免執行。有關列舉模組中的函式的部分也是如此。會將來源雜湊用於版本,有點類似於 git,但雜湊本身並未提供順序[5]。
一些其他語言有更正規化的模組版本支援,而相關議題已寫成文章
考慮一些程式 `myprogram.lua` 所使用的模組 foo
-- foo.lua return {_VERSION=1.0; f = function(x) return x^2 end; g = function(x) return x^3 end} -- myprogram.lua local foo = require 'foo' print(foo.g(2))
現在,假設推出新版本的模組 foo
而它打破了介面(這是不可取的,但有時候確實會發生)
-- foo.lua return {_VERSION=2.0; f = function(x) return x^2 end; g = function(x,y) return x^2*y end}
myprogram.lua
將不再能與新的 `foo` 正常運作。我們可以更新 `myprogram` 以與 `foo` 版本 2.0,甚至與版本 1.0 和版本 2.0 一起運作。方法有許多。以下方法只涉及一列變更
-- myprogram.lua local foo = require 'foo' local foo = setmetatable({g = (foo._VERSION < 2.0) and foo.g or function(x) return foo.g(x,x) end}, {__index = foo}) print(foo.g(2))
也可能只靠特點測試就能做到這一點,根本無需參考 `_VERSION`,例如,`(foo.g(2,0) == 8)`。相容性碼也可移至 `foo` 模組中
-- foo.lua local M = {_VERSION=2.0; f = function(x) return x^2 end; g = function(x,y) return x^2*y end} M.version1 = setmetatable({g = function(x) return M.g(x,x) end}, {__index = M}) return M
-- myprogram.lua (works with both foo version 1 and 2) local foo = require 'foo'; foo = foo.version1 or foo print(foo.g(2))
foo
也可以採用將版本 1 介面變為預設值,以避免中斷現有程式碼。如果介面中斷時,另一種可能性是將 foo
模組改名為其他名稱,例如 foo2
。
另外請注意,照著寫在程式中,兩個不同版本的 foo
介面可以在同時使用,例如兩個不同的模組。foo = foo.version1 or foo
,它會選用版本 1 的介面,屬於詞法範圍。