lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi Thijs,

On Tue, Apr 16, 2019 at 5:23 AM Thijs Schreijer <thijs@thijsschreijer.nl> wrote:
>
> LuaRocks certainly has this functionality. See the “luarocks.loader” module. It comes with LuaRocks.
>
> https://github.com/luarocks/luarocks/blob/master/src/luarocks/loader.lua
>
> It will use the LuaRocks dependency information to determine who is asking for a module, and what versions it is compatible with. And then it will go off and actually return/load a compatible version.
>
> Note that LuaRocks installs the modules in a file-tree that is searchable by Lua using the module environment variables, but it also has its own repository. So whenever a file is installed with the same name, the last one wins in the file-system. But in the repository they will remain available side-by-side. The LuaRocks loader is then smart enough to find the proper rock from the repository.
>
> Make sure you require “luarocks.loader” before loading the other modules.
>

Unfortunately this does not work for transitive dependencies,
specially using require, even if you take care to clear the require
cache.

Take these two simple rocks, each of which depends on another rock
which provides a 'complex' module:

------- uselcomplex-scm-1.rockspec ---------
package = "uselcomplex"
version = "scm-1"
source = {
  url = ""
}
description = {
   summary = "A rock which uses lcomplex's complex",
}
dependencies = {
   "lua >= 5.1, < 5.4",
   "lcomplex == 20180729-1"
}
build = {
  type = "builtin",
  modules = {
    uselcomplex = "uselcomplex.lua"
  }
}
-------

------- uselcomplex.lua ---------
local complex = require "complex"

return {
  test = function ()
    return complex.version
  end
}
-------

------- useluafft-scm-1.rockspec ---------
package = "useluafft"
version = "scm-1"
source = {
  url = ""
}
description = {
   summary = "A rock which uses luafft's complex",
}
dependencies = {
   "lua >= 5.1, < 5.4",
   "luafft == 1.2"
}
build = {
  type = "builtin",
  modules = {
    useluafft = "useluafft.lua"
  }
}
-------

------- useluafft.lua ---------
local complex = require "complex"

return {
  test = function ()
    return complex.version
  end
}
-------

You can install both in your rocks tree, and will get two different
"complex" modules. Now run this:

------- conflict.lua --------
require "luarocks.loader"

local uselcomplex = require "uselcomplex"
package.loaded.complex = nil
local useluafft = require "useluafft"

print(useluafft.test())
print(uselcomplex.test())
-------

This will print:

complex number library for Lua 5.3 / Jul 2018
complex number library for Lua 5.3 / Jul 2018

Changing the order of the two requires so  "useluafft" is required
first (again, cleaning the package cache for "complex" between the
two) prints (luafft's complex does not have a version field):

nil
complex number library for Lua 5.3 / Jul 2018

This is a tractable issue, the LuaRocks loader would need to keep a
stack where the top of the stack is the rock/version pair for the
module we are going to load next (popping the stack after loading it),
and for efficiency the manifest could also need to have a
(rock/version, bare_module) -> file_name mapping (which would replace
the need for the current dynamic loader context) or a (rock/version,
bare_module) -> other_rock/version mapping.

I think me and Hisham had discussed that several years ago in the
transition from LuaRocks requiring its own require function to playing
nice with the Lua module system and just adding a loader, but my
memory is fuzzy. :-)

If you remove the line to clear package.loaded it is even worse,
because require caches the module based on the bare module name:

nil
nil

That is only solvable by having LuaRocks provide its own require
function and monkey-patching the global require (which it used to).

--
Fabio Mascarenhas