runlua: Portable Lua Invocation for POSIX

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

runlua: Portable Lua Invocation for POSIX

William Ahern
Initial release of runlua.

PROJECT PAGE
============
http://www.25thandclement.com/~william/projects/runlua.html

DOWNLOAD
========
http://www.25thandclement.com/~william/projects/releases/runlua-20150122

DESCRIPTION
===========
runlua is a POSIX shell script for locating and invoking specific Lua
interpreter versions. For example, the environment's Lua 5.1 interpreter
might be named lua, lua5.1, lua51, luajit, luajit2.0.2, etc. runlua
automates this difficult task in a safe, portable manner. runlua is
regularly tested on Linux, OS X, Solaris, AIX, FreeBSD, NetBSD, and OpenBSD.
And runlua safely handles all special characters encountered in option
arguments, directory and command paths, and shell variables.

To execute a simple statement in either a Lua 5.2 or 5.3 interpreter:

   runlua -r5.2-5.3 -e "print(_VERSION)"

The command-line options to runlua are a superset of the standard Lua
interpreter. Run `runlua -h` for a description of each option.

Shebang (#!) Execution
----------------------
In addition to explicit invocation, runlua supports two modes of shebang
execution:

  #!/path/to/runlua -r5.2
  print"running Lua code!"

and

  #!/bin/sh
  echo "running shell code!"
  exec runlua -r5.2 -s"^exec" "$0" "$@"
  print "running Lua code!"

Only Linux and OS X support the first mode. The second is portable in
practice--although POSIX does not require sh to be located at
/bin/sh, it nonetheless can be invoked from that location on all the
environments I've tested.

Also, the first mode requires a fully qualified path name, whereas with the
second mode the shell code in the header can locate runlua dynamically. For
example, a regression or example script in a project repository might have a
header like

  #!/bin/sh
  PATH="${PATH}:$(dirname "$0")/../bin"
  exec "$(dirname "$0")/../contrib/runlua" -s"^exec" "$0" "$@"
  local mymodule = require"mymodule"
  -- ...

which will work regardless of the current working directory when invoking
the script.

USAGE
=====
Usage: runlua [-e:il:vEr:j:Js:tdpVh] [PATH [...]]
  -e STRING  execute statement
  -i         enter interactive mode after executing PATH
  -l STRING  require package
  -v         print interpreter version information
  -E         ignore environment variables
  -r RANGE   run specific Lua version
  -j RANGE   run specific LuaJIT version
  -J         exclude LuaJIT from candidate interpreters
  -s SKIP    line(s) to skip when loading script
  -t         preload POSIX threading library
  -d         enable debug logging
  -p         print path of Lua interpreter
  -V         print runlua version information
  -h         print this usage message

BNF:
  <PATH>    ::= <STRING>
  <RANGE>   ::= <VERSION> | [VERSION] "-" [VERSION]
  <VERSION> ::= <NUMBER> ["." <NUMBER> ["." <NUMBER>]]
  <SKIP>    ::= <NUMBER> | <PATTERN>
  <PATTERN> ::= <STRING>

Examples:
  -r5.2.1    only run PUC Lua 5.2.1 interpreter
  -r5.1      run any Lua 5.1 interpreter, including LuaJIT
  -r5.2-5.3  run any Lua 5.2 or 5.3 interpreter
  -r5.2-     run any Lua 5.2 or later interpreter
  -j2.1      only run LuaJIT 2.1 interpreter
  -s4        skip first 4 lines of script
  -s"^exec"  skip lines up to and including line matching ^exec

Report bugs to <[hidden email]>

Reply | Threaded
Open this post in threaded view
|

Re: runlua: Portable Lua Invocation for POSIX

Valerio
This looks cool. 
Is this going in the direction of being the 'RVM for Lua" (http://rvm.io) ? 

On Fri, Jan 23, 2015 at 6:59 AM, William Ahern <[hidden email]> wrote:
Initial release of runlua.

PROJECT PAGE
============
http://www.25thandclement.com/~william/projects/runlua.html

DOWNLOAD
========
http://www.25thandclement.com/~william/projects/releases/runlua-20150122

DESCRIPTION
===========
runlua is a POSIX shell script for locating and invoking specific Lua
interpreter versions. For example, the environment's Lua 5.1 interpreter
might be named lua, lua5.1, lua51, luajit, luajit2.0.2, etc. runlua
automates this difficult task in a safe, portable manner. runlua is
regularly tested on Linux, OS X, Solaris, AIX, FreeBSD, NetBSD, and OpenBSD.
And runlua safely handles all special characters encountered in option
arguments, directory and command paths, and shell variables.

To execute a simple statement in either a Lua 5.2 or 5.3 interpreter:

   runlua -r5.2-5.3 -e "print(_VERSION)"

The command-line options to runlua are a superset of the standard Lua
interpreter. Run `runlua -h` for a description of each option.

Shebang (#!) Execution
----------------------
In addition to explicit invocation, runlua supports two modes of shebang
execution:

  #!/path/to/runlua -r5.2
  print"running Lua code!"

and

  #!/bin/sh
  echo "running shell code!"
  exec runlua -r5.2 -s"^exec" "$0" "$@"
  print "running Lua code!"

Only Linux and OS X support the first mode. The second is portable in
practice--although POSIX does not require sh to be located at
/bin/sh, it nonetheless can be invoked from that location on all the
environments I've tested.

Also, the first mode requires a fully qualified path name, whereas with the
second mode the shell code in the header can locate runlua dynamically. For
example, a regression or example script in a project repository might have a
header like

  #!/bin/sh
  PATH="${PATH}:$(dirname "$0")/../bin"
  exec "$(dirname "$0")/../contrib/runlua" -s"^exec" "$0" "$@"
  local mymodule = require"mymodule"
  -- ...

which will work regardless of the current working directory when invoking
the script.

USAGE
=====
Usage: runlua [-e:il:vEr:j:Js:tdpVh] [PATH [...]]
  -e STRING  execute statement
  -i         enter interactive mode after executing PATH
  -l STRING  require package
  -v         print interpreter version information
  -E         ignore environment variables
  -r RANGE   run specific Lua version
  -j RANGE   run specific LuaJIT version
  -J         exclude LuaJIT from candidate interpreters
  -s SKIP    line(s) to skip when loading script
  -t         preload POSIX threading library
  -d         enable debug logging
  -p         print path of Lua interpreter
  -V         print runlua version information
  -h         print this usage message

BNF:
  <PATH>    ::= <STRING>
  <RANGE>   ::= <VERSION> | [VERSION] "-" [VERSION]
  <VERSION> ::= <NUMBER> ["." <NUMBER> ["." <NUMBER>]]
  <SKIP>    ::= <NUMBER> | <PATTERN>
  <PATTERN> ::= <STRING>

Examples:
  -r5.2.1    only run PUC Lua 5.2.1 interpreter
  -r5.1      run any Lua 5.1 interpreter, including LuaJIT
  -r5.2-5.3  run any Lua 5.2 or 5.3 interpreter
  -r5.2-     run any Lua 5.2 or later interpreter
  -j2.1      only run LuaJIT 2.1 interpreter
  -s4        skip first 4 lines of script
  -s"^exec"  skip lines up to and including line matching ^exec

Report bugs to <[hidden email]>


Reply | Threaded
Open this post in threaded view
|

Re: runlua: Portable Lua Invocation for POSIX

William Ahern
On Fri, Jan 23, 2015 at 09:13:06AM +0100, Valerio Schiavoni wrote:
> This looks cool.
> Is this going in the direction of being the 'RVM for Lua" (http://rvm.io) ?

That would be pretty ambitious. One step would be to add integration with
LuaRocks, but LuaRocks still has problems with portability, dealing with
multiple interpreters, and dealing with different installation layouts. I
haven't had time to dive into the LuaRocks code to untangle things because
I'm focused on making sure my stuff compiles everywhere.

The problem isn't the porting, per se. As long as you stick to standards,
don't unwittingly use non-portable Linux constructs, and minimize your
dependencies, it's not that difficult to write portable code.

The real issue is that initial testing and regression testing is really
tedious when juggling many different platforms, and it's compounded by
trying to support multiple different Lua versions with ad hoc installations.
I regularly test my projects on 7 different operating systems, and over 10
different hosts. That includes multiple different Linux distributions and
embedded firmwares. There are nearly as many different Lua installation
layouts[1]. Then multiply all that by the different Lua implementations: PUC
Lua 5.1, LuaJIT 2.0, PUC Lua 5.2, PUC Lua 5.3... it's insane!

The solution to the tedium is automation. I've dealt with most of the
compile-time problems with my luapath script. Among other things, luapath
can locate Lua headers, automatically reorder CPPFLAGS, and determine
install paths.[1] I can build my modules against all the Lua API 5.x APIs
simultaneously, in the same make invocation. IOW, I don't have to specify
different flags for different invocations. That's a feature I've found to be
quite useful to help me catch API issues earlier.

runlua was what I needed to help with runtime execution and specifically
automated regression testing. With luapath and runlua, I can finally begin
automating the task of pushing code changes, re-compiling, and running
regression tests across all the different platforms I support, without
having to rememeber the peculiarities of each platform's Lua installation or
have ad hoc support scripts on each host.

Once I actually have an automated testing work flow with my own projects,
then I might have some time to figure out why LuaRocks has been so difficult
for me to use.

[1] A few of the platforms I test on have pkg-config, either the
freedesktop.org implementation or an independent implementation (e.g.
OpenBSD). I don't think any of them have identical and correct variables
other than prefix and includedir, and that's even across Linux
distributions, not Linux vs BSD. libdir is wrong sometimes, as are some
other variables, and variables like INSTALL_CMOD, INSTALL_LMOD, version, etc
are not universal. In fact, on the same Ubuntu 14.04 box the set of
pkg-config variables for PUC Lua and LuaJIT are different, and the values
for libdir and INSTALL_LMOD are wrong for the LuaJIT package! So pkg-config
alone solves much less in practice than one might think, and of course it's
only useful where pkg-config exists _and_ the Lua intallation is recorded by
pkg-config.