7

I am trying to access SQLite databases from LuaLaTeX but am not able to load an installed lsqlite3 package. Here's what I tried so far:

  1. Took a brandnew Xubuntu 14.04 installation
  2. Installed a complete TeX Live 2014
  3. Installed Luarocks (which installed Lua5.1 on the way)
  4. Used Luarocks to install lsqlite3

    uwe@luabuntu:/media/uwe$ luarocks list

    Installed rocks:

    lsqlite3 0.9.1-2 (installed) - /usr/local/lib/luarocks/rocks

  5. Appended the package.path in the following TeX file:

    \documentclass{article}    
    \usepackage{luacode}
    
    \begin{document}
    
    \begin{luacode*}
      package.path="/usr/local/lib/luarocks/rocks;/usr/local/lib/luarocks/rocks/lsqlite3; 
      /usr/local/lib/lua/5.1/lsqlite3.so" .. package.path
    
      require("lsqlite3")
    \end{luacode*}
    
    Hello Lua!
    
    \end{document}
    

The error I receive is the following:

! LuaTeX error [\directlua]:3: module 'lsqlite3' not found:
  no field package.preload['lsqlite3']
  [luatexbase.loader] Search failed
  [kpse lua searcher] file not found: 'lsqlite3'
  [kpse C searcher] file not found: 'lsqlite3'
  [oberdiek.luatex.kpse_module_loader]-eroux Search failed
stack traceback:
  [C]: in function 'require'
  [\directlua]:3: in main chunk.
\luacode@dbg@exec ...code@maybe@printdbg {#1} #1 }

l.9 \end{luacode*}

What must be set to have LuaLaTeX find the lsqlite3 package?

EDIT

I have tried to adjust everything following the comments below:

  1. Adjusted /usr/local/texlive/2014/texmf.cnf
  2. Put everything into the example
  3. Still get the error about the missing lsqlite3 module with the following example

    \documentclass{article}
    \directlua{
    require "lualoader"
    }
    
    \usepackage{fontspec}
    \usepackage[english]{babel}
    \begin{document}
    
    \directlua{package.path='/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/home/uwe/.luarocks/share/lua/5.1/?.lua;/home/uwe/.luarocks/share/lua/5.1/?/init.lua;/usr/share/lua/5.1//?.lua;/usr/share/lua/5.1//?/init.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua'
    package.cpath='/usr/local/lib/lua/5.1/?.so;/home/uwe/.luarocks/lib/lua/5.1/?.so;./?.so;/usr/local/lib/lua/5.1/?.so;/usr/lib/x86_64-linux-gnu/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so'
    require "lualoader"
    }
    
    \directlua{%
    local sqlite3 = require("lsqlite3")
    local db = sqlite3.open_memory()
    
    db:exec[[
      CREATE TABLE test (id INTEGER PRIMARY KEY, content);
    
      INSERT INTO test VALUES (NULL, 'Hello World');
      INSERT INTO test VALUES (NULL, 'Hello Lua');
      INSERT INTO test VALUES (NULL, 'Hello Sqlite3')
    ]]
    
    for row in db:nrows("SELECT * FROM test") do
      tex.print(row.id .. " : ".. row.content )
    end
    
    }
    \end{document}
    
Stefan Pinnow
  • 29,535
Uwe Ziegenhagen
  • 13,168
  • 5
  • 53
  • 93
  • 1
    lsqlite3 is a binary module, at least in part. You probably have to set package.cpath as well. – Philipp Gesang Dec 21 '14 at 14:35
  • 2
    I have the strong feeling that package.path is ignored in kpathsea systems. - see http://tex.stackexchange.com/a/31904/243 – topskip Dec 21 '14 at 17:11
  • 1
    See also http://tex.stackexchange.com/q/48193/243 – topskip Dec 21 '14 at 17:13
  • @phg I have added the cpath setting, no success. Same error as before. – Uwe Ziegenhagen Dec 21 '14 at 22:06
  • @topskip: I checked both links but have no idea how to tell kpathsea to use the additional path(s) – Uwe Ziegenhagen Dec 21 '14 at 22:08
  • 1
    @UweZiegenhagen you call lualoader twice in your edit, paths must be set before it is required – michal.h21 Dec 25 '14 at 18:44
  • Still getting the error. I think I'll give up and take the route via Python. I thought it would be easier under Linux, well... – Uwe Ziegenhagen Dec 25 '14 at 19:51
  • 1
    The interpreted parts can be configured by means of the environment variable LUAINPUTS (example: export LUAINPUTS="~/code/tex/pgfplots/tex//:~/code/tex/pgf/generic//:"). There is also an environment variable CLUAINPUTS ... perhaps you need to adopt it? I know that DLL search paths tend to need native load procedures, in other words: environment variables (don't know how that works in LUA, though) – Christian Feuersänger Dec 25 '14 at 19:55

4 Answers4

12

It is possible to modify the package loading mechanism in Lua. In the case of LuaTeX, the kpse library is used to load modules instead of the default mechanism that uses package.path and package.cpath. So setting these variables will not have any effect. But nothing prevents us from using both methods using a Lua module, lualoader.lua. Save the snippet below as a file in the directory containing your document:

-- lualoader.lua
-- this is copied from luatexbase.loader
local make_loader = function(path, pos, loadfunc)
  local default_loader = package.searchers[pos]
  local loader = function(name)
    local file, _ = package.searchpath(name,path)
    if not file then
      local msg = "\n\t[lualoader] Search failed"
      local ret = default_loader(name)
      if type(ret) == "string" then
        return msg ..ret
      elseif type(ret) == "nil" then
        return msg
      else
        return ret
      end
    end
    local loader,err = loadfunc(file, name)
    if not loader then
      return "\n\t[lualoader] Loading error:\n\t"..err
    end
    return loader
  end
  package.searchers[pos] = loader
end

local binary_loader = function(file, name) local symbol = name:gsub("%.","") return package.loadlib(file, "luaopen"..symbol) end

make_loader(package.path,2,loadfile) make_loader(package.cpath,3, binary_loader)

We use the function make_loader to insert a function searching package.path or package.cpath at a specified index in the package.searchers table. 2 for lua files, 3 for binary modules. We also need to use different functions to load a module depending on its type. We use loadfile for Lua files and slightly more complicated function for binary modules that uses package.loadlib.

As an example, we can try loading lsqlite3 from LuaTeX:

\documentclass{article}
\directlua{
require "lualoader"
}

\usepackage{fontspec} \usepackage[english]{babel} \begin{document} \directlua{% local sqlite3 = require("lsqlite3")

local db = sqlite3.open_memory()

db:exec[[ CREATE TABLE test (id INTEGER PRIMARY KEY, content);

INSERT INTO test VALUES (NULL, 'Hello World'); INSERT INTO test VALUES (NULL, 'Hello Lua'); INSERT INTO test VALUES (NULL, 'Hello Sqlite3') ]]

for row in db:nrows("SELECT * FROM test") do tex.print(row.id .. " : ".. row.content ) end

} \end{document}

It is important to run LuaLaTeX with the --shell-escape flag, because binary libraries are blocked by default. With --shell-escape you can execute external programs and call binary libraries.

The example yields:

enter image description here

Edit:

As Uwe pointed out, luarocks as installed by some Linux package managers may not work correctly. I experienced this on Fedora, so I installed luarocks from source. Another solution may be to set package.path and package.cpath to the directories listed by the command

luarocks path

On my own system, this yields:

$ luarocks path
export LUA_PATH='/home/michal/.luarocks/share/lua/5.2/?.lua;/home/michal/.luarocks/share/lua/5.2/?/init.lua;/usr/local/share/lua/5.2/?.lua;/usr/local/share/lua/5.2/?/init.lua;/usr/share/lua/5.2/?.lua;/usr/share/lua/5.2/?/init.lua;/usr/lib/lua/5.2/?.lua;/usr/lib/lua/5.2/?/init.lua;./?.lua'
export LUA_CPATH='/home/michal/.luarocks/lib/lua/5.2/?.so;/usr/local/lib/lua/5.2/?.so;/usr/lib/lua/5.2/?.so;/usr/lib/lua/5.2/loadall.so;./?.so'

So a modified preamble would be:

\directlua{    
package.cpath = '/home/michal/.luarocks/lib/lua/5.2/?.so;/usr/local/lib/lua/5.2/?.so;/usr/lib/lua/5.2/?.so;/usr/lib/lua/5.2/loadall.so;./?.so'
package.path = '/home/michal/.luarocks/share/lua/5.2/?.lua;/home/michal/.luarocks/share/lua/5.2/?/init.lua;/usr/local/share/lua/5.2/?.lua;/usr/local/share/lua/5.2/?/init.lua;/usr/share/lua/5.2/?.lua;/usr/share/lua/5.2/?/init.lua;/usr/lib/lua/5.2/?.lua;/usr/lib/lua/5.2/?/init.lua;./?.lua'
require "lualoader"
}
michal.h21
  • 50,697
  • If I simply run the TeX code I get: "! LuaTeX error [\directlua]:1: module 'lualoader' not found: no field package.preload['lualoader'] [kpse lua searcher] file not found: 'lualoader' [kpse C searcher] file not found: 'lualoader'

    If I save the lua-loader code in the same folder as the TeX code I get an error as well: LuaTeX error [\directlua]:1: module 'luarocks.loader' not found: no field package.preload['luarocks.loader'] [kpse lua searcher] file not found: 'luarocks.loader' [kpse C searcher] file not found: 'luarocks.loader'

    – Uwe Ziegenhagen Dec 23 '14 at 16:47
  • @UweZiegenhagen luarocks.loader should be available if you have correctly installed luarocks. in this particular case it is not needed, because lsqlite3 is installed to directory with other Lua libraries, but it is needed for Lua modules installed through luarocks – michal.h21 Dec 23 '14 at 18:06
  • I installed luarocks via the Ubuntu package system. If I remove the line for the luarocks.loader I get the the following error again: ! LuaTeX error [\directlua]:1: module 'lsqlite3' not found: no field package.preload['lsqlite3'] – Uwe Ziegenhagen Dec 23 '14 at 18:35
  • @UweZiegenhagen ah, I installed luarocks from the sources, I had problems when it was installed by Fedora. You may need to set package.path and package.cpath in this case – michal.h21 Dec 23 '14 at 18:50
  • @UweZiegenhagen you're welcome :) – michal.h21 Dec 25 '14 at 20:05
  • your lualoader script is the only working example in the world wide web :) – Markus Sep 26 '15 at 19:45
  • I had a chance to try this code out, and I discovered an edge case which was causing me problems. I tried require("compat53.utf8") which is a binary library and I could not load it.The problem is in the binary_loader function above. The pattern for the symbol variable does not correctly convert compat53/utf8 to compat53_utf8 – vyom Oct 19 '16 at 11:58
  • @vyom does it work if you change that line to local symbol = base:gsub("[%.%/]","_")? – michal.h21 Oct 19 '16 at 12:10
  • To make it work, I changed it to base:gsub("/","_"), which is probably cruder. Your suggestion should work as well, but I could not think of a reason why the . character needs to be checked for the in the base string (and you don’t need %/, so the line could be something like local symbol = base:gsub("[%./]","_"). – vyom Oct 19 '16 at 12:35
  • @vyom I must say that I am actually surprised that the pattern in local base = file:match("/([^%.]+)%.[%w]+$") selects compat53/utf8 from string /usr/local/lib/lua/5.2/compat53/utf8.so§ – michal.h21 Oct 19 '16 at 13:48
  • Yes, that is a another problem. I think the binary loader needs to be implemented differently. Maybe what we should really be doing is looking at the name parameter, and constructing the symbol based on that. – vyom Oct 19 '16 at 13:56
  • @vyom you are right, the updated loader seems to work correctly – michal.h21 Oct 19 '16 at 19:40
  • Will you update the answer? – vyom Oct 20 '16 at 05:38
  • @vyom I've just updated it – michal.h21 Oct 20 '16 at 08:38
  • I added a new answer below, to point out the existence of luapackageloader on CTAN which should take care of this for most people. – vyom May 29 '17 at 03:48
  • Note: you need to pass --shell-escape to the executable (for example lualatex) otherwise it would just return nil and cause some error such as "[\directlua]:20: attempt to concatenate a nil value (local 'err')" – user202729 Nov 01 '21 at 16:50
  • @user202729 thanks for the notice. you are right, in the recent versions of LuaTeX, binaries are blocked by default. – michal.h21 Nov 01 '21 at 16:56
6

As of May 2017, there is a package on CTAN called luapackageloader, that allows you to modify the default package searching behavior in LuaTeX.

Once you load luapackageloader, doing a require will first try the default kpse based searcher. If the module isn’t found, it will also try to load the package from Lua’s package.path and package.cpath values.

Usage example

\documentclass{article}    
\usepackage{luacode}
\usepackage{luapackageloader}

\begin{document}

\begin{luacode*}
  package.path="/usr/local/lib/luarocks/rocks;/usr/local/lib/luarocks/rocks/lsqlite3; 
  /usr/local/lib/lua/5.1/lsqlite3.so" .. package.path

  require("lsqlite3")
\end{luacode*}

Hello Lua!

\end{document}

Additional note: you can do eval $(luarocks path) on your shell to set the package path using environment variables which the Lua interpreter will pick up automatically. That way, you do not need to fiddle with the package.path yourself.

vyom
  • 447
1

Based on comments on this question, I have the following line in /usr/local/texlive/2014/texmf.cnf:

CLUAINPUTS  = .;$SELFAUTOLOC/lib/{$progname,$engine,}/lua//;/usr/lib/{$progname,$engine,}/lua//

But my luarocks list gives:

Installed rocks:

lsqlite3
   0.9.1-2 (installed) - /usr/lib/luarocks/rocks-5.2

so you may need to adjust the second path according to your installation.

nplatis
  • 2,090
1

Summary

Searching for the package

  • In normal Lua, a package is searched in package.path and package.cpath.

  • In LuaTeX, a package is by-default searched with kpse.

  • There are 2 ways to modify where LuaTeX searches for packages

  1. modify behavior of kpse

    Modify texmf.cnf file, variables LUAINPUTS and CLUAINPUTS.

  2. make Lua load from package.(c)path, then modify those

    • With \usepackage{luapackageloader}, packages are searched in both kpse and package.(c)path.

    • The value of package.(c)path can be set manually by the user within the Lua code itself, but it's better to set environment variables e.g. LUA_PATH, LUA_CPATH.

      If you run eval $(luarocks path) in bash, all processes spawned from that bash shell will inherit the environment variables.

Binary package

  • If the package requires binary files, --shell-escape must be passed.

Versioning

  • The package version must be compatible with Lua version. Do print(_VERSION) within Lua to check.

    To specify particular version to luarocks, do e.g. luarocks --lua-version 5.3 path.

user202729
  • 7,143