模块版本控制

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 中,use 语句类似于 Lua 的 require,可以传递版本号(例如“use foo 1.23;[2]),它会被转发到模块,模块实际上可以对它做任何事情(例如,检查它或更改接口行为)。在 Lua 中,可以使用local foo = require 'foo' (1.23)来实现类似的功能,但这并不常见。 LuaRocks 提供了一种可选的 require 替代形式,它不仅可以检查,还可以推荐在安装多个版本的情况下加载哪个版本 ([luarocks.require]),但这不再被广泛使用。

从模块中提取版本通常涉及加载模块并读取其_VERSION变量。如果模块不可信,您可以将其加载到沙箱中,或者通过进行基本的源代码分析(程序分析)来避免执行。枚举模块中的函数也是如此。源代码的哈希可以代替版本使用,类似于 git,但哈希本身不提供排序[5]

一些其他语言具有更正式的模块版本控制支持,并且已经针对它们编写了相关问题。

维护接口兼容性

考虑一个名为 foo 的模块,它被某些程序 myprogram.lua 使用。

-- 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 GMT (差异)