其实我安装 lua5.1 只是为了方便兼容 neovim 和 luajit, 这两货目前都只支持lua 5.1

其实根本原因还是在于 luajit 多年没更新(如果我没记错,现在已经2021年了,luajit最后一次发版还是2017年?),没有兼容最新版本的lua 5.4 。

而 neovim 实际是因为依赖luajit 才导致依赖 lua 5.1的,也是没办法了.

初战翻车

环境说明: 操作系统是 Fedora 33, 默认的 lua包是 lua 5.4版本的, luarocks 也是从包管理器dnf直接安装的.

luarocks install --local fzy 失败:

Error: Failed finding Lua header files. You may need to install them or configure LUA_INCDIR.

为了方式与luajit lua5.1 配合工作,同时避免包管理问题, 还是卸载系统的 luarocks, 自己编译安装吧.

sudo rpm -evh luarocks

然后,我们不要用Fedora 33系统自带的luarocks, 它的默认luarocks system配置是 lua 5.4的.

Fedora安装lua5.1和luajit

# compat-lua is lua5.1
sudo dnf install compat-lua compat-lua-libs compat-lua-devel lua-filesystem-compat lua-socket-compat lua5.1-compat53 \
lua5.1-luv-devel lua5.1-http lua5.1-lpeg lua5.1-luaossl \
lua5.1-mmdb lua5.1-mpack lua5.1-psl lua5.1-sec lua5.1-basexx lua5.1-binaryheap lua5.1-bitop \
luajit

Ubuntu20.10安装lua5.1和luajit

sudo apt-get install -y libluajit-5.1-dev liblua5.1-0-dev

编译安装

当前最新版本的LuaRocks 版本为 3.5.0, 可以去 https://luarocks.github.io/luarocks/releases/ 查看。

Fedora 33 下可以用 --with-lua-include=/usr/include/lua-5.1/, 而这个路径在 Ubuntu 20.10 下面是 /usr/include/lua5.1, 因此还是不要加--with-lua-include 参数为好,让它自动查找,我们只需要指定 --lua-version=5.1 即可.

curl -LZO https://luarocks.org/releases/luarocks-3.5.0.tar.gz
tar xvzpf luarocks-3.5.0.tar.gz
cd luarocks-3.5.0

❯ ./configure --lua-version=5.1

Configuring LuaRocks version 3.5.0...

Lua version detected: 5.1
Lua interpreter found: /usr/bin/lua-5.1
lua.h found: /usr/include/lua-5.1/lua.h
unzip found in PATH: /usr/bin

Done configuring.

LuaRocks will be installed at......: /usr/local
LuaRocks will install rocks at.....: /usr/local
LuaRocks configuration directory...: /usr/local/etc/luarocks
Using Lua from.....................: /usr

* Type make and make install:
  to install to /usr/local as usual.
* Type make bootstrap:
  to install LuaRocks into /usr/local as a rock.

可以看到,配置完后luarocks提示我们,有两种安装方式. 到底用哪一种呢?

万能的stackoverflow 很快给了我们答案:

Should Icompile luarocks ‘as usual’ or as a ‘rock’?

As you can see from the ChangeLog: make bootstrap is now an advertised option for installing LuaRocks itself as a rock on Unix systems That means that if you go this way you can upgrade luarocks via luarocks install:

Those of you who installed LuaRocks 2.1.0 or later by using make bootstrap on Unix may upgrade by simply running: luarocks install luarocks

from [ANN] LuaRocks 2.2.1.

因此, make bootstrap 是推荐方式, 这种方式安装后你甚至可以用luarocks install luarocks 来更新 luarocks自己。

❯ sudo make bootstrap
mkdir -p "build"
mkdir -p .luarocks
cp ./build/config-5.1.lua .luarocks/config-5.1.lua
rm -f src/luarocks/core/hardcoded.lua
echo "#!/bin/sh" > luarocks
echo "unset LUA_PATH LUA_PATH_5_2 LUA_PATH_5_3 LUA_PATH_5_4 LUA_CPATH LUA_CPATH_5_2 LUA_CPATH_5_3 LUA_CPATH_5_4" >> luarocks
echo 'LUAROCKS_SYSCONFDIR="/usr/local/etc/luarocks" LUA_PATH="/home/ttys3/build/luarocks-3.5.0/src/?.lua;;" exec "/usr/bin/lua-5.1" "/home/ttys3/build/luarocks-3.5.0/src/bin/luarocks" --project-tree="/home/ttys3/build/luarocks-3.5.0/lua_modules" "[email protected]"' >> luarocks
chmod +rx ./luarocks
./luarocks init

Initializing project 'luarocks-3.5.0' for Lua 5.1 ...
-----------------------------------------------------

Checking your Lua installation ...
Adding entries to .gitignore ...
Preparing ./.luarocks/ ...
Wrote .luarocks/config-5.1.lua
Preparing ./lua_modules/ ...
./luarocks already exists. Not overwriting it!
Preparing ./lua for version 5.1...
mkdir -p "/usr/local/etc/luarocks"
install -m 644 "build/config-5.1.lua" "/usr/local/etc/luarocks/config-5.1.lua"
./luarocks make --tree="/usr/local"

No existing manifest. Attempting to rebuild...
luarocks 3.5.0-1 is now installed in /usr/local (license: MIT)

可以看到, luarocks 被安装在 /usr/local/share/lua/5.1/luarocks

用户级目录在:~/.luarocks

系统配置在 /usr/local/etc/luarocks 头文件在 /usr/include/lua-5.1

lua5.1 (dnf 安装的) 本身的lib在: /lib64/lua/5.1

❯ luarocks --version
/usr/local/bin/luarocks 3.5.0
LuaRocks main command-line interface

注: 其实像 ArchLinux 和 Fedora 本身带的 luarocks 包是比较新的,是可以不用自己编译的。

小试牛刀

luarocks install --local fzy
Installing https://luarocks.org/fzy-0.4-1.rockspec
Cloning into 'fzy-lua'...
remote: Enumerating objects: 24, done.
remote: Counting objects: 100% (24/24), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 24 (delta 0), reused 12 (delta 0), pack-reused 0
Receiving objects: 100% (24/24), 22.69 KiB | 505.00 KiB/s, done.
Note: switching to 'a3f1dd75725b535e6b00af84048c7e066432f530'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false


fzy 0.4-1 depends on lua >= 5.1 (5.1-1 provided by VM)
gcc -O2 -fPIC -I/usr/include/lua-5.1 -c src/fzy_native.c -o src/fzy_native.o -DLUA_COMPAT_5_1
gcc -O2 -fPIC -I/usr/include/lua-5.1 -c src/match.c -o src/match.o -DLUA_COMPAT_5_1
gcc -shared -o fzy_native.so src/fzy_native.o src/match.o
No existing manifest. Attempting to rebuild...
fzy 0.4-1 is now installed in /home/ttys3/.luarocks (license: MIT)

OK, 安装成功,我们看下都写入了啥文件:

❯ tree ~/.luarocks
/home/ttys3/.luarocks
├── config-5.1.lua
├── lib
│   └── luarocks
│       └── rocks-5.1
│           ├── fzy
│           │   └── 0.4-1
│           │       ├── fzy-0.4-1.rockspec
│           │       ├── rock_manifest
│           │       └── test
│           │           ├── benchmark.lua
│           │           ├── files.txt
│           │           └── test.lua
│           └── manifest
├── lib64
│   └── lua
│       └── 5.1
│           └── fzy_native.so
└── share
    └── lua
        └── 5.1
            ├── fzy.lua
            └── fzy_lua.lua

12 directories, 10 files

~/.luarocks/lib/luarocks/rocks-5.1 相当于一个包管理数据库,里面记录了安装的rocks的元数据。

~/.luarocks/lib64/lua/5.1/ 下面是 so 文件

~/.luarocks/share/lua/5.1/ 下面是 *.lua文件

❯ luarocks config --scope user config_files
{
   nearest = "/home/ttys3/.luarocks/config-5.1.lua",
   system = {
      file = "/etc/luarocks/config-5.4.lua",
      found = true
   },
   user = {
      file = "/home/ttys3/.luarocks/config-5.1.lua",
      found = true
   }
}

配置文件可以用 LUAROCKS_CONFIG 指定

用户配置一般是 /home/ttys3/.luarocks/config-5.1.lua

❯ bat /usr/local/etc/luarocks/config-5.1.lua
-- LuaRocks configuration

rocks_trees = {
   { name = "user", root = home .. "/.luarocks" };
   { name = "system", root = "/usr/local" };
}
lua_interpreter = "lua-5.1";
variables = {
   LUA_DIR = "/usr";
   LUA_INCDIR = "/usr/include/lua-5.1";
   LUA_BINDIR = "/usr/bin";
}

注意这里的rocks_trees, 分为user和system级别的,并不是一个直接的路径,实际上通过默认安装生成的文件可以发现, 它是 rocks_trees.root .. "/share/lua/5.1/" .. "*.lua文件或目录" 如:

❯ tree -L 1 /usr/local/share/lua/5.1/
/usr/local/share/lua/5.1/
└── luarocks

1 directory, 0 files


❯ tree /home/ttys3/.luarocks/share/lua/5.1/
/home/ttys3/.luarocks/share/lua/5.1/
├── fzy.lua
└── fzy_lua.lua

0 directories, 2 files

默认配置lib_modules_path是错的, 这个值默认在Fedora和Ubuntu下面都是/lib/lua/5.1, 然而这个路径在这两个系统上都是不存在的。 Fedora下通过dnf安装的lua 5.1 是/lib64/lua/5.1, 而Ubuntu 20.10 通过apt 安装的,则是在 /usr/share/lua/5.1

❯ luarocks config --scope user lib_modules_path
/lib/lua/5.1
❯ ls /lib/lua/5.1
ls: cannot access '/lib/lua/5.1': No such file or directory
# Fedora
❯ ls /lib64/lua/5.1
bit.so  compat53/  _cqueues.so  lfs.so  lpeg.so  lpeg.so.1.0.2  luv.so  mime/  mpack.so  _openssl.so  psl.so  socket/  ssl.so

# Ubuntu
❯ ls /usr/share/lua/5.1
basexx.lua  compat53/  cqueues.lua  http/  json.lua        ltn12.lua  mime.lua  openssl.lua  socket/     ssl/
cjson/      cqueues/   fifo.lua     json/  lpeg_patterns/  lxp/       openssl/  re.lua       socket.lua  ssl.lua

Fedora 33 下修正:

❯ luarocks config --scope user lib_modules_path /lib64/lua/5.1
Wrote
	lib_modules_path = "/lib64/lua/5.1"
to
	/home/ttys3/.luarocks/config-5.1.lua

Ubuntu 20.10 下修正:

❯ luarocks config --scope user lib_modules_path /usr/share/lua/5.1
Wrote
	lib_modules_path = "/usr/share/lua/5.1"
to
	/home/ttys3/.luarocks/config-5.1.lua

如果没有,这里luarocks会自动生成配置文件.

rocks 默认安装到用户目录下

https://github.com/luarocks/luarocks/issues/356

You mean –local?

You can also put local_by_default=true in the ~/.luarocks/config.lua file and this flag will be assumed by default. It’s great for running LuaRocks as non-root.

最终的文件:

❯ bat /home/ttys3/.luarocks/config-5.1.lua
lib_modules_path = "/lib64/lua/5.1"
local_by_default = true

Troubleshoot

❯ luarocks config
/usr/bin/lua5.4: /usr/share/lua/5.4/luarocks/fs.lua:95: local 'each_platform' is not callable (a nil value)
stack traceback:
	/usr/share/lua/5.4/luarocks/fs.lua:95: in upvalue 'load_platform_fns'
	/usr/share/lua/5.4/luarocks/fs.lua:122: in function 'luarocks.fs.init'
	/usr/share/lua/5.4/luarocks/cmd.lua:500: in function 'luarocks.cmd.run_command'
	/usr/bin/luarocks:38: in main chunk
	[C]: in ?

错误看着很奇怪,同时安装了lua5.1 和 lua5.4, 但是 lua5.1 应该路径优先在前面的,为什么会报 lua5.4 的错误? 后面发现是将配置改错了:

variables = {
    LUA = "/usr/bin/lua5.1";
    LUA_DIR = "/usr";
    LUA_BINDIR = "/usr/bin";
}

里面的 ; 被我删除了,因为写 Golang 的习惯是,末尾的 ; 是编译器自动加的,习惯性地就删除了。 但是在这个 table 里,实际上是 lua 里的 ; 相当于 Golang 里面的 ,, 修正配置,就 OK 了

My config

config-5.1.lua

-- ~/.luarocks/config-5.1.lua

-- default config see https://github.com/luarocks/luarocks/blob/d4e82aae9f411d5d2cac10f7e043a0c3c6180eb1/src/luarocks/core/cfg.lua#L167

--  http://lua-users.org/wiki/LuaRocksConfig
--  https://github.com/luarocks/luarocks/wiki/Dependencies
--  https://github.com/luarocks/luarocks/wiki/Documentation
 

lua_version = "5.1"

local_by_default = true

lua_interpreter = "lua" .. lua_version

-- do not prepend `/usr` to lua_modules_path or lib_modules_path
-- no need to change the default generally.
-- otherwise both `deploy_lib_dir` and `deploy_lua_dir` value will be wrong
----------------------------------------------------------------
-- remember: 
-- `home_tree = "/home/ttys3/.luarocks"`
-- `deploy_lua_dir = home_tree .. lua_modules_path`
-- `deploy_lib_dir = home_tree .. lib_modules_path`
----------------------------------------------------------------
-- lua_modules_path = "/share/lua/"..lua_version
-- lib_modules_path = "/lib/lua/"..lua_version

-- we need correct `lib_modules_path` here
lib_modules_path = "/lib64/lua/"..lua_version

-- for 5.1 ROCKS_TREE is `/home/ttys3/.luarocks/lib/luarocks/rocks-5.1`
-- so rocks_subdir is `lib/luarocks/rocks-5.1`, no need to change the default
-- set it to full absolute path is incorrect, it does not need the `~/.luarocks` part
-- otherwise both `variables.ROCKS_TREE` and `rocks_dir` value will be wrong
-- remember:  `rocks_dir = home_tree .. rocks_subdir`
-- system: /usr/lib/luarocks/rocks-5.1
-- user: /home/ttys3/.luarocks/lib/luarocks/rocks-5.1
-- rocks_subdir = "/lib/luarocks/rocks-"..lua_version

connection_timeout = 30  -- 0 = no timeout

variables = {
    LUA = "/usr/bin/lua" .. lua_version,
    LUA_DIR = "/usr",
    LUA_BINDIR = "/usr/bin",
}

参考文档

https://github.com/luarocks/luarocks/wiki/Installation-instructions-for-Unix

http://lua-users.org/wiki/LuaRocksConfig

https://github.com/luarocks/luarocks/wiki/config-file-format

https://stackoverflow.com/a/29340739/13267147