模組版本控管

lua-users home
wiki

Lua 標準函式庫 (_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 > 00900500110.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(例如 LuaRocksLuaDist、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 的介面,屬於詞法範圍。

--DavidManura

另請參閱


最近變更 · 偏好設定
編輯 · 歷史記錄
最後編輯時間:2012 年 4 月 10 日上午 12:43 格林威治時間 (差異)